PostbackX Docs

REST API

Read your reporting data over HTTPS — authentication, conventions, endpoints and error handling.

Updated 2026-07-30 Use with AI Markdown source

The API returns your own campaign data as JSON, from the same queries that back the dashboard — so the API and the UI always agree.

The authoritative, machine-readable definition is the OpenAPI specification. Most HTTP clients and code generators can consume it directly. This page is the orientation.

https://api.postbackx.com/v1

Authentication#

Create a key on API & MCP, then send it as a bearer token:

bash
curl -s "https://api.postbackx.com/v1/reports/totals?start_date=2026-07-01&end_date=2026-07-20" \
  -H "Authorization: Bearer pbx_live_YOUR_KEY"

Keys are read-only and bound to a single organization. If your account belongs to several, a key reads the one it was issued for; to read another, issue a key there.

Keep keys on a server or in a scheduled job. Never in browser code, and never in a committed file.

Conventions#

Worth reading once — they explain most first-attempt failures.

  • Dates are required. Every reporting endpoint needs an explicit start_date and

end_date. There is no default window.

  • Ranges are inclusive on both ends, and formatted YYYY-MM-DD.
  • Time zones are yours to choose. Pass an IANA name as timezone; it defaults to

UTC, which is probably not how you read your own reports.

  • Lists are comma-separated strings, e.g. campaign_ids=9c1f...,4ab2....
  • Payloads are wrapped in data, with context under meta.
  • Metrics are pre-computed. roas, conversion_rate, epc and cpa come back

calculated. Recomputing them from the raw fields is how a report starts disagreeing with the dashboard.

  • Rate limit is 1000 requests per hour per credential.

What is there#

Reporting/reports/totals for one headline row, /reports/timeseries for a trend, /reports/campaigns and /reports/offers for per-entity rows, /reports/breakdown for the ad-platform hierarchy down to the creative, and /reports/calls for inbound call attribution.

Entities/campaigns, /offers, /offer-sources, /tracking-domains, /organizations. Use these to turn the ids that reporting returns into names.

Raw data/clicks and /conversions, one row per record. These are the heavy ones: page through them and store the results rather than re-pulling the same window.

WritesPATCH /campaigns/{id} and PATCH /offers/{id}, accepting status only (active, paused or archived). That is deliberately the entire write surface, so an automation can pause a loser without any risk of rewriting its configuration.

Conversions cannot be created through this API. They arrive at the postback URL, which also handles deduplication, click matching and delivery to the ad platforms — a row written directly would reach none of those.

The full parameter list for each endpoint is in the specification and in the searchable explorer on API & MCP.

Reading the breakdown endpoint#

/reports/breakdown is the one with a shape worth explaining, because it returns the whole tree at once rather than one level per call.

Every row carries its own level, entity_key and parent_key. You rebuild the hierarchy by matching a row's parent_key to the entity_key above it. Pass level to get a single rung instead of everything.

Level names are platform-specific, because each platform names its own hierarchy (adset on Facebook, tt_adgroup on TikTok). The cross-platform shorthands campaign, adgroup and creative fan out across all of them, and are what you want unless you are targeting one platform.

Errors#

Every failure uses one envelope:

json
{
  "error": {
    "type": "validation_failed",
    "message": "One or more parameters were rejected.",
    "fields": [
      { "field": "start_date", "code": "required", "message": "start_date is required." }
    ]
  }
}

Branch on error.type, never on the message. Messages are written for people and will be reworded; the types are the contract.

TypeStatusWhat to do
validation_failed422Read error.fields; each names the parameter and why.
unauthorized401The key is missing, malformed or revoked.
forbidden403Valid key, wrong organization.
not_found404No such endpoint or record.
rate_limited429Back off for error.retry_after_seconds.
internal_error500, 502Our side. Retry with exponential backoff.

Not writing code#

If the goal is answers rather than a integration, connect an AI assistant instead — same data, no client to maintain. The app's API & MCP page also has ready-made snippets for Google Sheets, Python, Make and Zapier.