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

Swipe horizontally to view full table
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]
Swipe horizontally for full code

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 .md source. 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();
Swipe horizontally for full code

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:

Swipe horizontally to view full table
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.

Swipe horizontally to view full table
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.

Swipe horizontally to view full table
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"
Swipe horizontally for full code

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
Swipe horizontally for full code

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.
Swipe horizontally for full code

Where to go next

Help us improve this article or request new support guides.