Purchase contact details (e.g., business emails/phones) for a set of profile IDs you selected via Profiles — Search.
Designed for compliant, auditable access with clear attribution.
Base URL: https://api.salescaddy.ai/api
Endpoint
POST /profiles/purchase
Headers
Authorization: Bearer <token>
— requiredX-On-Behalf-Of-User: [email protected]
— required (billing/audit attribution)Content-Type: application/json
Request body (JSON)
{
"profileIds": ["prof_001","prof_002","prof_003"]
}
Send 1–N profile IDs. The API returns a result per requested ID, including status and (when successful) contact data.
Tip: deduplicateprofileIds
on the client before sending.
Example — Purchase contacts
curl -sS -X POST "https://api.salescaddy.ai/api/profiles/purchase" -H "Authorization: Bearer $TOKEN" -H "X-On-Behalf-Of-User: [email protected]" -H "Content-Type: application/json" -d '{ "profileIds": ["prof_001","prof_002","prof_003"] }'
const res = await fetch("https://api.salescaddy.ai/api/profiles/purchase", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.TOKEN}`,
"X-On-Behalf-Of-User": "[email protected]",
"Content-Type": "application/json"
},
body: JSON.stringify({ profileIds: ["prof_001","prof_002","prof_003"] })
});
console.log(await res.json());
import os, requests
payload = {"profileIds": ["prof_001","prof_002","prof_003"]}
r = requests.post("https://api.salescaddy.ai/api/profiles/purchase",
headers={
"Authorization": f"Bearer {os.environ['TOKEN']}",
"X-On-Behalf-Of-User": "[email protected]",
"Content-Type": "application/json"
},
json=payload)
print(r.json())
using System.Text;
using System.Net.Http.Headers;
var http = new HttpClient();
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", TOKEN);
http.DefaultRequestHeaders.Add("X-On-Behalf-Of-User","[email protected]");
var payload = new StringContent("{"profileIds":["prof_001","prof_002","prof_003"]}", Encoding.UTF8, "application/json");
var res = await http.PostAsync("https://api.salescaddy.ai/api/profiles/purchase", payload);
Console.WriteLine(await res.Content.ReadAsStringAsync());
Sample response (trimmed):
{
"results": [
{
"profileId": "prof_001",
"status": "purchased",
"creditsCharged": 1,
"fullName": "Alex Johnson",
"title": "VP Data Platform",
"companyDomain": "hilton.com",
"emails": [
{"value":"[email protected]","type":"work","confidence":0.95, "verified": true}
],
"phones": [
{"value":"+1-555-123-4567","type":"work","confidence":0.82}
],
"linkedinUrl": "https://www.linkedin.com/in/alexjohnson/"
},
{
"profileId": "prof_002",
"status": "already_owned",
"creditsCharged": 0
},
{
"profileId": "prof_003",
"status": "not_available",
"creditsCharged": 0,
"message": "No contact data available for this profile"
}
],
"summary": {
"requested": 3,
"purchased": 1,
"alreadyOwned": 1,
"notAvailable": 1,
"creditsCharged": 1
}
}
Statuses
purchased
— contact data returned and credited now.already_owned
— you already have access; not re‑charged.not_available
— no data available at this time (no charge).
Example — Idempotent retries (transient errors)
# Create a deterministic request key from sorted IDs to deduplicate on your side
REQ_KEY=$(echo '["prof_001","prof_002","prof_003"]' | jq -c 'sort|tostring' | shasum | awk '{print $1}')
curl -sS -X POST "https://api.salescaddy.ai/api/profiles/purchase" -H "Authorization: Bearer $TOKEN" -H "X-On-Behalf-Of-User: [email protected]" -H "Idempotency-Key: $REQ_KEY" -H "Content-Type: application/json" -d '{ "profileIds": ["prof_001","prof_002","prof_003"] }'
// Sort IDs to build a stable idempotency key for safe retries
const ids = ["prof_001","prof_002","prof_003"].sort();
const key = require("crypto").createHash("sha1").update(JSON.stringify(ids)).digest("hex");
const res = await fetch("https://api.salescaddy.ai/api/profiles/purchase", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.TOKEN}`,
"X-On-Behalf-Of-User": "[email protected]",
"Idempotency-Key": key,
"Content-Type": "application/json"
},
body: JSON.stringify({ profileIds: ids })
});
console.log(await res.json());
import hashlib, json, os, requests
ids = ["prof_001","prof_002","prof_003"]
key = hashlib.sha1(json.dumps(sorted(ids)).encode()).hexdigest()
r = requests.post("https://api.salescaddy.ai/api/profiles/purchase",
headers={
"Authorization": f"Bearer {os.environ['TOKEN']}",
"X-On-Behalf-Of-User": "[email protected]",
"Idempotency-Key": key,
"Content-Type": "application/json"
},
json={"profileIds": ids})
print(r.json())
using System.Security.Cryptography;
using System.Text.Json;
var ids = new[]{"prof_001","prof_002","prof_003"}.OrderBy(x=>x).ToArray();
var jsonIds = JsonSerializer.Serialize(ids);
var hash = BitConverter.ToString(SHA1.HashData(System.Text.Encoding.UTF8.GetBytes(jsonIds))).Replace("-","").ToLowerInvariant();
http.DefaultRequestHeaders.Add("Idempotency-Key", hash);
var res2 = await http.PostAsync("https://api.salescaddy.ai/api/profiles/purchase",
new StringContent(JsonSerializer.Serialize(new{ profileIds=ids }), System.Text.Encoding.UTF8, "application/json"));
Console.WriteLine(await res2.Content.ReadAsStringAsync());
While the API may not require an
Idempotency-Key
, including one is a best practice for POSTs you might retry on 429/5xx.
After purchase
- Call Profiles — Details to fetch the full profile objects by
profileId
. - Respect consent & compliance in your downstream systems; store
creditsCharged
for audit. - Consider caching purchased contacts to avoid re‑purchasing.
Errors
Code | Meaning | How to fix |
---|---|---|
400 | Bad request | Validate JSON body and profileIds (non‑empty array of strings). |
401 | Unauthorized | Provide/refresh Bearer token. |
402 | Payment required | Insufficient credits/plan; top‑up or contact support. |
403 | Forbidden | Missing X-On-Behalf-Of-User or insufficient permissions. |
404 | Not found | One or more profileIds not recognized. |
409 | Conflict | Duplicate request (if Idempotency‑Key reused with different payload). |
413 | Payload too large | Reduce number of profileIds per request; batch in smaller chunks. |
429 | Rate limit exceeded | Retry with exponential backoff; respect Retry-After . |
500 | Internal server error | Retry later; contact support if persistent. |