Introduction to API
The Retreaver Core API is a RESTful, paginated API for automating your Retreaver account. Change where calls are routed, manage campaigns, export call logs, and most other things you would normally do through our account portal.
Retreaver has four integration surfaces
| Approach | Best for | Access |
|---|---|---|
| Core API (this section) | Full read/write automation of your account: campaigns, targets, affiliates, numbers, and exporting calls | Your account-wide Core API key |
| Postback Keys | Letting third parties (buyers, affiliates) perform one specific action (converting a call, reserving a target, writing tags) without full account access | Scoped, action-specific tokens |
| Retreaver.js | Tracking visitors and displaying tracking phone numbers on your landing pages | Client-side JavaScript, no API key |
| Call Data Writing | Applying tags and data to calls in real time from external systems (e.g. enriching a live call from your CRM) | Data-writing postback URLs |
If you just need to display numbers on a page or fire a conversion pixel, start with Retreaver.js or Postback Keys. If you want to access your call and campaign data use the Core API.
Getting started
Example API URL:
https://api.retreaver.com/api/v4/calls.json?api_key=[api_key]&company_id=[company_id]You’ll find your Core API key on the API access page in the account portal. You can find your company ID here. See the Authentication guide for full details.
Warning
Your Core API key grants unrestricted access to your entire account. Never expose it publicly. This is the key difference from Postback Keys, which are scoped to a single action on a single resource and are safe to hand to third parties. If your Core API key leaks, reset it.
LLMs and AI
This documentation is designed to be consumed by AI agents. Every page is available as Markdown: Append .md to any documentation URL to get the raw Markdown source of that page:
https://learn.retreaver.com/api/calls.md
llms.txt
We publish llms.txt, a machine-readable index of our documentation that lets an LLM discover every page and fetch its Markdown:
- /api/llms.txt — every Core API reference page, with links to its
.mdsource. Start here for API integration work. - /llms.txt — the site-wide index: API reference plus support guides, interactive demos, and news.
Warning
Any AI generated script must be used responsibly and respect our concurrency limits. Retreaver is designed to handle high traffic and we welcome API usage, but warning: spamming our servers using AI generated code may result in accounts being banned or API keys being reset. Talk to our support staff if you need help.
API Code Example
This here is an example script for getting started with the API. It will export the last 2 days of calls to a calls.json file.
// Retrieves all calls created in the last 2 hours using the Calls API v4.
//
// How to run:
// 1. Save to a file, e.g. retreaver_example.js
// 2. Edit the file changing API_KEY and COMPANY_ID
// 3. Run with: node retreaver_example.js
//
// Requires Node.js 18+ (uses the built-in global fetch — no npm install needed).
// Configuration
const API_KEY = "your-api-key"; // <- change to your api key https://retreaver.com/user/edit/api_access
const COMPANY_ID = "123"; // <- Change to your company ID visible here https://retreaver.com/company
// 2 hours in the past. Use RFC 3339 (iso8601) as a format
// 2026-02-25T18:08:31Z
// Note that the api accepts other params like
// created_at_end, caller, client_cid, call_flow_events
// and that calls could be sorted in different ways. Consult the documentation.
const createdAtStart = new Date(Date.now() - 2 * 3600 * 1000).toISOString();
const perPage = 100;
// Sorting by created_at ascending keeps the result set stable while we
// paginate (new calls get appended to later pages rather than shifting
// the pages we've already fetched).
const params = new URLSearchParams({
api_key: API_KEY,
created_at_start: createdAtStart,
company_id: COMPANY_ID,
per_page: perPage,
sort_by: "created_at",
order: "asc",
});
const BASE_URL = `https://api.retreaver.com/api/v4/calls.json?${params}`;
console.log(BASE_URL);
async function fetchCalls(page) {
const response = await fetch(`${BASE_URL}&page=${page}`);
if (response.ok) {
return response.json();
} else {
console.log(`Error: ${response.status} - ${response.statusText}`);
return null;
}
}
async function main() {
const allCalls = [];
let page = 1;
console.log("Starting call retrieval...");
// Paginate using the page param until an empty page is returned
while (true) {
const calls = await fetchCalls(page);
if (!calls) break;
if (calls.length === 0) {
console.log(`Page ${page}: No more calls, stopping.`);
break;
}
allCalls.push(...calls);
console.log(
`Page ${page}: Processed ${calls.length} calls (Total: ${allCalls.length})`,
);
page += 1;
// Safety break if you want to test with just a few pages first
// if (page > 5) break;
}
console.log(`\nFinished! Total calls processed: ${allCalls.length}`);
console.log();
console.log(
`${"UUID".padEnd(36)} ${"Caller".padEnd(14)} ${"Status".padEnd(10)} Date`,
);
console.log("-".repeat(90));
for (const item of allCalls) {
const call = item.call;
console.log(
`${(call.uuid ?? "").padEnd(36)} ` +
`${(call.caller ?? "").padEnd(14)} ` +
`${(call.status ?? "").padEnd(10)} ` +
`${call.created_at ?? ""}`,
);
}
const now = new Date();
const pad = (n) => String(n).padStart(2, "0");
const timestamp =
`${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(now.getDate())}_` +
`${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}`;
const outputFile = `calls_${timestamp}.json`;
const { writeFileSync } = await import("node:fs");
writeFileSync(outputFile, JSON.stringify(allCalls, null, 2));
console.log(`\nDone -- Saved ${allCalls.length} calls to: ./${outputFile}`);
}
main();# Retrieves all calls created in the last 2 hours using the Calls API v4.
#
# How to run:
# 1. Save to a file, e.g. retreaver_example.rb
# 2. Edit the file changing API_KEY and COMPANY_ID
# 3. Run with: ruby retreaver_example.rb
require 'net/http'
require 'uri'
require 'json'
# Configuration
API_KEY = "your-api-key" # <- Change to your api key https://retreaver.com/user/edit/api_access
COMPANY_ID = "123" # <- Change to your company ID visible here https://retreaver.com/company
# 2 hours in the past. Use RFC 3339 (iso8601) as a format
# 2026-02-25T18:08:31Z
# Note that the api accepts other params like
# created_at_end, caller, client_cid, call_flow_events
# and that calls could be sorted in different ways. Consult the documentation.
created_at_start = Time.at(Time.now.to_i - 2 * 3600).utc.iso8601
per_page = 100
# Sorting by created_at ascending keeps the result set stable while we
# paginate (new calls get appended to later pages rather than shifting
# the pages we've already fetched).
BASE_URL = "https://api.retreaver.com/api/v4/calls.json?api_key=#{API_KEY}&created_at_start=#{created_at_start}&company_id=#{COMPANY_ID}&per_page=#{per_page}&sort_by=created_at&order=asc"
puts BASE_URL
def fetch_calls(page)
uri = URI.parse("#{BASE_URL}&page=#{page}")
response = Net::HTTP.get_response(uri)
if response.is_a?(Net::HTTPSuccess)
JSON.parse(response.body)
else
puts "Error: #{response.code} - #{response.message}"
nil
end
end
all_calls = []
page = 1
puts "Starting call retrieval..."
# Paginate using the page param until an empty page is returned
loop do
calls = fetch_calls(page)
break unless calls
if calls.empty?
puts "Page #{page}: No more calls, stopping."
break
end
all_calls.concat(calls)
puts "Page #{page}: Processed #{calls.length} calls (Total: #{all_calls.length})"
page += 1
# Safety break if you want to test with just a few pages first
# break if page > 5
end
puts "\nFinished! Total calls processed: #{all_calls.length}"
puts
puts "%-36s %-14s %-10s %s" % ["UUID", "Caller", "Status", "Date"]
puts "-" * 90
all_calls.each do |item|
call = item["call"]
puts "%-36s %-14s %-10s %s" % [
call["uuid"],
call["caller"],
call["status"],
call["created_at"]
]
end
timestamp = Time.now.strftime("%Y%m%d_%H%M%S")
output_file = "calls_#{timestamp}.json"
File.write(output_file, JSON.pretty_generate(all_calls))
puts "\nDone -- Saved #{all_calls.length} calls to: ./#{output_file}"# Retrieves all calls created in the last 2 hours using the Calls API v4.
#
# How to run:
# 1. Save to a file, e.g. retreaver_example.py
# 2. Edit the file changing API_KEY and COMPANY_ID
# 3. Run with: python3 retreaver_example.py
#
# Requires Python 3.6+ (uses only the standard library — no pip install needed).
import json
from datetime import datetime, timedelta, timezone
from urllib.parse import urlencode
from urllib.request import urlopen
from urllib.error import HTTPError, URLError
# Configuration
API_KEY = "your-api-key" # <- Change to your api key https://retreaver.com/user/edit/api_access
COMPANY_ID = "123" # <- Change to your company ID visible here https://retreaver.com/company
# 2 hours in the past. Use RFC 3339 (iso8601) as a format
# 2026-02-25T18:08:31Z
# Note that the api accepts other params like
# created_at_end, caller, client_cid, call_flow_events
# and that calls could be sorted in different ways. Consult the documentation.
created_at_start = (
(datetime.now(timezone.utc) - timedelta(hours=2))
.strftime("%Y-%m-%dT%H:%M:%SZ")
)
per_page = 100
# Sorting by created_at ascending keeps the result set stable while we
# paginate (new calls get appended to later pages rather than shifting
# the pages we've already fetched).
params = urlencode({
"api_key": API_KEY,
"created_at_start": created_at_start,
"company_id": COMPANY_ID,
"per_page": per_page,
"sort_by": "created_at",
"order": "asc",
})
BASE_URL = f"https://api.retreaver.com/api/v4/calls.json?{params}"
print(BASE_URL)
def fetch_calls(page):
try:
with urlopen(f"{BASE_URL}&page={page}") as response:
return json.load(response)
except HTTPError as error:
print(f"Error: {error.code} - {error.reason}")
return None
except URLError as error:
print(f"Error: {error.reason}")
return None
all_calls = []
page = 1
print("Starting call retrieval...")
# Paginate using the page param until an empty page is returned
while True:
calls = fetch_calls(page)
if calls is None:
break
if not calls:
print(f"Page {page}: No more calls, stopping.")
break
all_calls.extend(calls)
print(f"Page {page}: Processed {len(calls)} calls (Total: {len(all_calls)})")
page += 1
# Safety break if you want to test with just a few pages first
# if page > 5: break
print(f"\nFinished! Total calls processed: {len(all_calls)}")
print()
print(f"{'UUID':<36} {'Caller':<14} {'Status':<10} Date")
print("-" * 90)
for item in all_calls:
call = item["call"]
print(
f"{call.get('uuid', ''):<36} "
f"{call.get('caller', ''):<14} "
f"{call.get('status', ''):<10} "
f"{call.get('created_at', '')}"
)
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
output_file = f"calls_{timestamp}.json"
with open(output_file, "w") as f:
json.dump(all_calls, f, indent=2)
print(f"\nDone -- Saved {len(all_calls)} calls to: ./{output_file}")Working with the API
Nomenclature
Retreaver’s API uses some different terms for data than we typically show in our UI, as we offer a toggle in the Store to swap between Performance Marketing terms and standard terminology:
| API | Performance Marketing Edition | Default |
|---|---|---|
| Affiliate | Publisher | Source |
| Target | Buyer | Contact Handler/Call Endpoint |
| Target Group | Buyer Group | Handler Group |
Version
The API is available both at versioned paths (/api/v1 through /api/v4) and at legacy versionless paths. We recommend using the versioned paths. For sake of clarity we’ll refer to this API henceforth as:
Retreaver Core API
Multi-format
The Retreaver Core API can be accessed by JSON or XML. Simply change the file extension and Content-Type header to match your preferences.
We recommend using JSON since XML is archaic, has high overhead, and is generally awful.
| Format | Content-Type | Extension |
|---|---|---|
| JSON | application/json | json |
| XML | text/xml | xml |
Our IDs vs Customer IDs
For convenience, some objects can be accessed both by the IDs set by our customers and by our internal IDs.
Affiliates, Targets, and Campaigns can be accessed via customer editable IDs as afid, tid, and cid respectively.
The id property in the JSON/XML document refers to our internal ID.
| Object | Our Internal ID URL | Customer Editable ID URL | Customer Editable ID Accessor |
|---|---|---|---|
| Affiliate | /affiliates/{id} | /affiliates/afid/{afid} | afid |
| Target | /targets/{id} | /targets/tid/{tid} | tid |
| Campaign | /campaigns/{id} | /campaigns/cid/{cid} | cid |
These URLs work with GET, PUT, and DELETE HTTP verbs.
Please note, in many sections of the documentation below, we are referring to the Customer Editable ID URL.
Paginated
Retreaver is a RESTful paginated API. Use the page parameter (default 1) to select a page and the per_page parameter (default 25, maximum 100; values above 100 are clamped to 100) to control page size. Each relevant index response will have a Link HTTP header present.
Link: <https://api.retreaver.com/api/v1/calls.json?api_key=[api_key]&company_id=1&sort_by=created_at&order=asc&page=6996>; rel="last", <https://api.retreaver.com/api/v1/calls.json?api_key=[api_key]&company_id=1&sort_by=created_at&order=asc&page=2>; rel="next"
By parsing the Link header, you can determine the last, next, and previous pages.
Concurrency limits
Requests are processed with a concurrency cap — typically a maximum of 4 concurrent requests per account. Sequential requests are always safe; if you parallelize (for example, fetching multiple pages of calls at once), keep it to 4 threads or fewer. Please consult with the Retreaver team for the current limits on your account before implementing a high-thread-count integration.
Trees
Please note, for brevity, in appropriate places we have truncated the API output by eliminating properties of certain objects. We’re also trying to save some trees in case someone actually tries to print this.
Data Retrieval & Real-Time Sync
1. Batch Retrieval (Polling)
Retrieving a full day’s worth of data can take from a few seconds to several minutes, depending on call volume.
Recommendation: For historical syncing, poll the API every 10 minutes to retrieve calls updated within that specific window.
Note
A single call record may be updated multiple times as it progresses through different lifecycle stages. High-frequency polling ensures you capture these state changes.
2. Real-Time Updates (Webhooks)
For immediate synchronization, we recommend using Retreaver Webhooks. This shifts your integration from a “Pull” to a “Push” model, reducing overhead and latency.
Workflow: Create a webhook that triggers at the end of a call (after all pixels have fired).
Benefits: Instead of constantly polling, your system is instantly notified at key moments, such as when a call starts, when it ends, or during specific lifecycle events.
Benchmarks
As a rough guide: sequentially, an account with ~1.5k calls takes about 35 seconds to export (16 requests at ~2s each). Running 4 threads in parallel (the concurrency cap) brings that down to roughly 10–15 seconds.
Script Example: Benchmarking your export
To measure execution time yourself, wrap the retrieval loop from the script example above in Ruby’s Benchmark module. Add require 'benchmark' to the top of the script, then:
require 'benchmark'
all_calls = []
time = Benchmark.realtime do
current_url = BASE_URL
page_count = 0
while current_url
page_count += 1
result = fetch_calls(current_url)
break unless result
all_calls.concat(result[:calls])
puts "Page #{page_count}: Processed #{result[:calls].length} calls (Total: #{all_calls.length})"
current_url = result[:next_url]
end
end
puts "Finished! #{all_calls.length} calls processed in #{time.round(2)} seconds"Execution Logs: Sequence Processing - 19.23 seconds for ~772 calls
Starting call retrieval...
Page 1: Processed 100 calls (Total: 100)
Page 2: Processed 100 calls (Total: 200)
Page 3: Processed 100 calls (Total: 300)
Page 4: Processed 100 calls (Total: 400)
Page 5: Processed 100 calls (Total: 500)
Page 6: Processed 100 calls (Total: 600)
Page 7: Processed 100 calls (Total: 700)
Page 8: Processed 72 calls (Total: 772)
Finished! Total calls processed: 772
Total time elapsed: 19.23 seconds
Execution Logs: Parallel Processing - 8.84 seconds for ~1000 calls in 4 threads
Thread 3 finished Page 4 (100 calls)
Thread 0 finished Page 1 (100 calls)
Thread 1 finished Page 2 (100 calls)
Thread 2 finished Page 3 (100 calls)
Thread 0 finished Page 6 (100 calls)
Thread 3 finished Page 5 (100 calls)
Thread 1 finished Page 7 (100 calls)
Thread 2 finished Page 8 (100 calls)
Thread 3 finished Page 10 (39 calls)
Thread 0 finished Page 9 (100 calls)
Final Result: 939 calls processed in 8.84 seconds.
Where to go next
- Exporting or syncing calls → the Calls API (build against v4) and the polling/webhook guidance above.
- Managing your account programmatically → Campaigns, Targets, Affiliates, and Numbers.
- Giving a buyer or third party limited access → Postback Keys.
- Tracking visitors on landing pages → Retreaver.js.
Help us improve this article or request new support guides.