API reference

RTB ping and post

The endpoints a publisher's platform calls to sell you a call in real time: ping for a bid, post to claim it, release it and look the call up — and, for the buyer, post a conversion.

This is the surface a publisher's system talks to. It is authenticated with a publisher key — one publisher on one campaign — rather than with your API key, and it answers in its own envelope: ok: true with the result, or ok: false with an error code and a message. The walkthrough is in Ping and post; this page is the field-by-field reference.

Base URL and authentication

Base URL
https://api.buy3.io/api/rtb/v1
  • Send the publisher key as Authorization: Bearer b3k_live_…. The key identifies the publisher and the campaign, so neither appears in a request.
  • Bodies are JSON, at most 32 KB.
  • If the key requires it, sign each request with the X-Buy3-Timestamp, X-Buy3-Nonce and X-Buy3-Signature headers — see Keys and security.
  • Every request, accepted or refused, is a row in the ping ledger that you and the publisher can both read.

Two per-call keys

A claimed call has two keys, one for each side of the trade, and neither opens what the other does.

KeyWho holds itWhat it opens
Lookup key, b3p_…The publisher. It comes back from /post as lookup.key, with lookup.url.Look a call up — and nothing else. Presented to the conversion endpoint it is refused with 403 lookup_key_cannot_convert.
Conversion key, b3c_…The buyer the call was sent to. It travels only on the buyer's own leg of the call: the X-Buy3-Postback-Key SIP header, and {{postback.key}} (with {{postback.url}}) in the buyer's custom headers and screen pop.Post a conversion, and look the call up. It is minted for this call and the target it was sent to, so only the buyer the call ended up with can convert it.
No response a publisher receives carries a conversion key. A buyer reached on a plain phone number receives no headers and so no key; their conversions are recorded by the seller, in the console or through the REST API.

Rate limits

Pings are limited per key, per minute, hour and day. Responses carry RateLimit-Policy and RateLimit headers; over the limit the answer is 429 with the reject code 1024 and a Retry-After header. The defaults are in Limits and can be changed on each key.

Endpoints

Ping for a bid

POST/api/rtb/v1/ping

Describe a call and get back what it is worth. A win reserves a slot for that caller until the bid expires; it does not return a destination — claim the bid with a post for that. The same request is also accepted as a GET with the fields as query parameters, for platforms that cannot send a body. A GET is not a way round signing: on a key that requires signatures it is signed over its raw query string, exactly as it was sent, without the leading ?.

Publisher key. Send Authorization: Bearer <publisher key> — the key issued for one publisher on one campaign.

Request body

NameTypeDescription
callerIdOptionalstringThe caller's number. Required when the campaign requires a caller ID; a bid made without one carries a warning.E.164, such as +12145550142
stateOptionalstringThe caller's state, as a two-letter code.
zipOptionalstringThe caller's ZIP code.
cityOptionalstringThe caller's city.
sipOkOptionalbooleanWhether you can deliver the call over SIP. When true, the post may answer with a sipAddress.
exposeCallerIdOptionalbooleanWhether the caller's number may be passed on to the buyer.
publisherRefOptionalstringYour own reference for this lead. It is stored with the call and shown in your portal.
externalCallIdOptionalstringYour phone system's id for the call.
testOptionalbooleanMarks the ping as a test. The response carries a warning saying so.
tagsOptionalobjectKey-value pairs describing the caller. Any other top-level scalar field you send is kept as a tag too, so an existing payload usually works unchanged.at most 40 tags · key up to 64 characters, value up to 512

Example request

curl -X POST "https://api.buy3.io/api/rtb/v1/ping" \
  -H "Authorization: Bearer b3k_live_your_publisher_key" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 7c9e6679-7425-40de-944b-e07fc1f90ae7" \
  -d '{
    "callerId": "+12145550142",
    "state": "TX",
    "zip": "75201",
    "sipOk": true,
    "publisherRef": "lead-88231",
    "tags": { "utm_source": "search", "age_band": "65plus" }
  }'

Responses

  • 200A bid. expiresInSeconds is how long the price and the reserved slot are held.
  • 200 (no bid)No bid. reject.code is one of the reject codes and never changes meaning.
{
  "ok": true,
  "auctionId": "0b9c6c1e-7f0a-4a55-9d0e-3c1b8a2f4d10",
  "bidId": "0f8fad5b-d9cb-469f-a165-70867728950e",
  "payout": 38.5,
  "payoutCents": 3850,
  "currency": "USD",
  "expiresInSeconds": 30,
  "expiresAt": "2026-09-20T14:03:57.000Z",
  "expiresAtEpochMs": 1789913037000,
  "nextStep": "post",
  "terms": [
    {
      "code": 101,
      "description": "Call must connect"
    },
    {
      "code": 100,
      "description": "Minimum connected duration 90 seconds",
      "seconds": 90
    }
  ],
  "campaign": {
    "id": "c2b7e1a0-6f3d-4b1e-8a55-0d9c4e7f1a23",
    "name": "Medicare — inbound"
  }
}

Errors

StatusCodeWhen
401missing_keyNo Authorization: Bearer header.
401invalid_keyThe key is not recognised, or has been revoked and its grace period is over.
4014000–4003The key requires signed requests and the signature, timestamp or nonce did not check out. See Keys and security.
403wrong_protocolThe key is not enabled for this protocol.
403publisher_not_liveThe publisher is not live.
403campaign_not_liveThe campaign is not live.
403ip_not_allowedThe request came from outside the key's IP allowlist.
400malformed_requestThe body is not valid JSON.
409bid_in_flightAn identical ping is being scored right now. Wait for it instead of sending it again.
409caller_already_reservedA live bid is already held for this caller on this campaign.
429rate_limitedThe key is over one of its ping limits. Retry-After says how long to wait.

Post to claim a bid

POST/api/rtb/v1/post

Claim a winning bid before it expires and receive the destination to send the caller to. The Idempotency-Key header is required: a retried post must never create a second call for the same caller, and you cannot detect that from your side. If the winning target has filled since the ping, the platform tries the runners-up before giving up, and says so with failedOver.

Publisher key. Send Authorization: Bearer <publisher key> — the key issued for one publisher on one campaign.

Request body

NameTypeDescription
bidIdRequireduuidThe bidId from the ping.
callerIdOptionalstringThe caller's number. If the ping carried one, this must match it.up to 32 characters
externalCallIdOptionalstringYour phone system's id for the call.up to 120 characters
tagsOptionalobjectFurther tags learned since the ping.

Example request

curl -X POST "https://api.buy3.io/api/rtb/v1/post" \
  -H "Authorization: Bearer b3k_live_your_publisher_key" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 1b4e28ba-2fa1-41d2-883f-0016d3cca427" \
  -d '{
    "bidId": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "callerId": "+12145550142",
    "externalCallId": "pbx-5531902"
  }'

Responses

  • 201The bid is claimed. Send the caller to sipAddress if you can deliver over SIP, otherwise to phoneNumber. On a call tracking campaign phoneNumber is always a number of the platform's own — your lane's tracking number, or the campaign's ingress number — never the buyer's line, because a caller who reaches the buyer directly is a call the platform never sees and nobody is paid for. lookup is yours: the call's lookup URL and a read-only per-call key for Look a call up. It cannot report a conversion — that is the buyer's, on a key only the buyer is sent.
{
  "ok": true,
  "callId": "5f0c1c8e-2a44-4d0a-9d0f-7b3f0a6c9e12",
  "ref": "CL-204817",
  "bidId": "0f8fad5b-d9cb-469f-a165-70867728950e",
  "payout": 38.5,
  "payoutCents": 3850,
  "sipAddress": "sip:0f8fad5bd9cb469fa16570867728950e@sip.example.net",
  "phoneNumber": "+18335550119",
  "phoneNumberNoPlus": "18335550119",
  "expiresInSeconds": 30,
  "expiresAt": "2026-09-20T14:04:27.000Z",
  "terms": [
    {
      "code": 101,
      "description": "Call must connect"
    },
    {
      "code": 100,
      "description": "Minimum connected duration 90 seconds",
      "seconds": 90
    }
  ],
  "lookup": {
    "url": "https://api.buy3.io/api/rtb/v1/calls/CL-204817",
    "key": "b3p_4f1d2c3b4a5e6f70"
  }
}

Errors

StatusCodeWhen
401missing_keyNo Authorization: Bearer header.
401invalid_keyThe key is not recognised, or has been revoked and its grace period is over.
4014000–4003The key requires signed requests and the signature, timestamp or nonce did not check out. See Keys and security.
403wrong_protocolThe key is not enabled for this protocol.
403publisher_not_liveThe publisher is not live.
403campaign_not_liveThe campaign is not live.
403ip_not_allowedThe request came from outside the key's IP allowlist.
400idempotency_key_requiredNo Idempotency-Key header was sent.
400validation_errorbidId is missing or is not a UUID.
404unknown_bidNo such bid for this key.
409bid_already_claimedThe bid has already been claimed and is no longer open. A straightforward repeat of a post that succeeded is not this error — it answers 201 with the original body again.
409caller_id_mismatchThe callerId does not match the one the bid was made for.
409no_capacity_at_postThe winning target filled before the post, and no runner-up could take the call.
409no_destinationA call tracking lane with no tracking number of the platform's own and no SIP address to send the caller to. Refused before a slot is taken or a call is written, rather than answered with a destination that would not be tracked. Carries reject: 1018 and warning 250.
410bid_expiredThe bid's expiresInSeconds had passed.

Release a bid

POST/api/rtb/v1/release

Give a bid back when the caller is not coming — they hung up, or you sold the call elsewhere. It frees the reserved slot immediately instead of when the bid expires. Releasing a bid that is already closed is not an error.

Publisher key. Send Authorization: Bearer <publisher key> — the key issued for one publisher on one campaign.

Request body

NameTypeDescription
bidIdRequireduuidThe bid to release.
reasonOptionalstringWhy, in your own words. Shown in the ping ledger.up to 120 characters

Example request

curl -X POST "https://api.buy3.io/api/rtb/v1/release" \
  -H "Authorization: Bearer b3k_live_your_publisher_key" \
  -H "Content-Type: application/json" \
  -d '{ "bidId": "0f8fad5b-d9cb-469f-a165-70867728950e", "reason": "caller_hung_up" }'

Responses

  • 200The bid was released.
  • 200 (already closed)The bid had already been claimed, released or expired. status says which.
{
  "ok": true,
  "released": true
}

Errors

StatusCodeWhen
401missing_keyNo Authorization: Bearer header.
401invalid_keyThe key is not recognised, or has been revoked and its grace period is over.
4014000–4003The key requires signed requests and the signature, timestamp or nonce did not check out. See Keys and security.
403wrong_protocolThe key is not enabled for this protocol.
403publisher_not_liveThe publisher is not live.
403campaign_not_liveThe campaign is not live.
403ip_not_allowedThe request came from outside the key's IP allowlist.
404unknown_bidNo such bid for this key.

Post a conversion

POST/api/rtb/v1/conversion

The buyer's postback: tell the seller that a call they received converted. It takes no publisher key and is authorised by the buyer's own per-call conversion key, b3c_…, which is sent only on the buyer's leg of the call — the X-Buy3-Postback-Key SIP header, or {{postback.key}} in the buyer's custom headers and screen pop. The key is minted for this call and the target it was sent to, so it converts the call only for the buyer the call ended up with. The lookup key /post returns to the publisher cannot report a conversion. A call takes one conversion.

No API key. No Authorization header. The request authenticates itself with a value in its body or URL, described below.

Request body

NameTypeDescription
keyRequiredstringThe buyer's per-call conversion key, b3c_…, as it arrived on their leg of the call.up to 80 characters
callIdRequireduuidThe call being reported on.
convertedOptionalbooleanWhether the call converted. Defaults to true.
valueOptionalnumberWhat the conversion was worth, in dollars.0 or more
dispositionOptionalstringYour outcome label, such as policy_sold.up to 60 characters
reasonOptionalstringFree-text detail.up to 200 characters
occurredAtOptionaldateWhen the conversion happened. Defaults to now.

Example request

curl -X POST "https://api.buy3.io/api/rtb/v1/conversion" \
  -H "Content-Type: application/json" \
  -d '{
    "key": "b3c_7d41e0a29c5b3f86d2e17a0c94b6f25e8d3a1c07",
    "callId": "5f0c1c8e-2a44-4d0a-9d0f-7b3f0a6c9e12",
    "converted": true,
    "value": 420.00,
    "disposition": "policy_sold"
  }'

Responses

  • 200The conversion was recorded and the call re-settled.
{
  "ok": true,
  "callId": "5f0c1c8e-2a44-4d0a-9d0f-7b3f0a6c9e12",
  "ref": "CL-204817",
  "billable": true,
  "payoutCents": 3850
}

Errors

StatusCodeWhen
400malformed_requestThe body is not valid JSON.
400validation_errorkey or callId is missing, or callId is not a UUID.
403lookup_key_cannot_convertThe key is the publisher's lookup key for this call — the one /post returns. It can only read the call back; a conversion is reported by the buyer, with the key sent on the buyer's own leg.
404unknown_callNo call has that callId, or the key is not the conversion key of the buyer the call ended up with. Any other wrong key answers 404, not 403, so the endpoint cannot be used to discover which call ids exist.
409already_convertedThe call already has a conversion. convertedAt says when.
410window_closedConversions close 72 hours after the call ends.

Check a key

GET/api/rtb/v1/whoami

Everything an integrator would otherwise have to ask you: which publisher and campaign the key belongs to, whether both are live, the key's limits, whether it must sign requests, its IP allowlist and the exact URLs to call. endpoints.lookup is the call lookup the per-call key from /post opens; the conversion endpoint is not listed, because it is the buyer's. A recognised but blocked key answers 200 with ok: false and the problem named; only an unknown key is an error.

Publisher key. Send Authorization: Bearer <publisher key> — the key issued for one publisher on one campaign.

Example request

curl "https://api.buy3.io/api/rtb/v1/whoami" \
  -H "Authorization: Bearer b3k_live_your_publisher_key"

Responses

  • 200The key's identity and limits.
{
  "ok": true,
  "problem": null,
  "protocol": "rtb_v1",
  "environment": "live",
  "publisher": {
    "id": "7d3c2b1a-0f9e-4d8c-b7a6-5e4d3c2b1a09",
    "name": "Brightpath Media",
    "status": "active"
  },
  "campaign": {
    "id": "c2b7e1a0-6f3d-4b1e-8a55-0d9c4e7f1a23",
    "name": "Medicare — inbound",
    "status": "live",
    "requireCallerId": false,
    "requiredTags": []
  },
  "key": {
    "prefix": "b3k_live_9f2a",
    "label": "Production",
    "requireSignature": false,
    "ipAllowlist": []
  },
  "limits": {
    "perMinute": 120,
    "perHour": 5000,
    "perDay": 50000,
    "maxLiveBidsPerCaller": 1,
    "bidTtlSeconds": 30
  },
  "endpoints": {
    "ping": "https://api.buy3.io/api/rtb/v1/ping",
    "post": "https://api.buy3.io/api/rtb/v1/post",
    "release": "https://api.buy3.io/api/rtb/v1/release",
    "lookup": "https://api.buy3.io/api/rtb/v1/calls/{ref}"
  }
}

Errors

StatusCodeWhen
401missing_keyNo Authorization: Bearer header.
401invalid_keyThe key is not recognised.

Look a call up

GET/api/rtb/v1/calls/:ref

Fetch a call's details, whatever a phone system stripped from the SIP headers on the way. Two keys open it: the publisher's lookup key, b3p_…, that /post returned as lookup.key, and the buyer's conversion key, b3c_…, that travelled on their leg of the call. Because both sides can read it, the response carries only what both may see: the winning bid is named by its id and its duration term, and never by its amount. The caller's number is included only when the campaign exposes caller ID and the call has connected.

No API key. No Authorization header. The request authenticates itself with a value in its body or URL, described below.

Path parameters

NameTypeDescription
refRequiredstringThe call's public reference, such as CL-204817, or its id.

Query parameters

NameTypeDescription
keyOptionalstringThe lookup key or the buyer's conversion key, when it cannot be sent as Authorization: Bearer <key>.

Example request

curl "https://api.buy3.io/api/rtb/v1/calls/CL-204817" \
  -H "Authorization: Bearer b3p_4f1d2c3b4a5e6f70"

Responses

  • 200The call, as much of it as both sides of the trade may read.
{
  "ok": true,
  "ref": "CL-204817",
  "callId": "5f0c1c8e-2a44-4d0a-9d0f-7b3f0a6c9e12",
  "status": "completed",
  "startedAt": "2026-09-20T14:03:21.000Z",
  "answeredAt": "2026-09-20T14:03:27.000Z",
  "caller": {
    "number": "+12145550142",
    "state": "TX"
  },
  "campaign": {
    "id": "c2b7e1a0-6f3d-4b1e-8a55-0d9c4e7f1a23",
    "name": "Medicare — inbound"
  },
  "publisherRef": "lead-88231",
  "tags": {
    "utm_source": "search",
    "age_band": "65plus"
  },
  "bid": {
    "id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "durationSeconds": 90
  }
}

Errors

StatusCodeWhen
401missing_keyNo key was sent in the header or the query.
404unknown_callNo call matches that reference, or the key is neither its lookup key nor the conversion key of the buyer it ended up with. The two answer the same, so a guessed reference learns nothing.