The ping ledger
Every ping in and out is a row you can open: the request, the answer, the latency and why it ended the way it did — for you, and for each publisher on their own side.
After this page you can find any ping by its reference, read what was actually sent and answered, tell a slow buyer from a badly filtered publisher, and export the evidence for a conversation with either.
On most platforms a no-bid is a number on a dashboard. Here every ping is a ledger row: what was sent, what came back, how long it took and why it ended the way it did. It is under Ping log in the console and in the Pings API.
Two ledgers
| Ledger | Console | One row is |
|---|---|---|
| Inbound | Incoming · from publishers | One request a publisher made to you — an auction, whatever it decided. |
| Outbound | Outgoing · to buyers | One request you made to one buyer's bidding endpoint. A single auction that asked three buyers is three rows here and one row there. |
direction=inbound or direction=outbound. Inbound is the default.A third kind of record sits beside the inbound ledger: requests refused before an auction existed — a bad signature, an address off the allowlist, a key on the wrong surface. They have no auction to belong to and are counted separately, because they are the refusals a publisher can most often fix themselves.
What an inbound row holds
curl "https://api.buy3.io/api/v1/pings?direction=inbound&outcome=no_bid&reasonCode=1008&from=2026-09-19T05:00:00Z" \
-H "Authorization: Bearer $BUY3_API_KEY"{
"id": "3f2e1d0c-9b8a-4765-8432-10fedcba9876",
"ref": "AU-100483",
"at": "2026-09-20T14:03:11.000Z",
"outcome": "no_bid",
"reason": {
"code": 1008,
"reason": "state_not_targeted",
"sentence": "That state is not being bought."
},
"campaign": { "id": "6a2d1f0e-8b7c-4c55-a1d3-0e9f8a7b6c5d", "name": "Acme Health inbound" },
"publisher": { "id": "c4e1a2b3-9d8f-4f6e-b5a4-1c2d3e4f5a6b", "name": "Northwind Media" },
"callerNumber": "••••••0142",
"callerState": "AR",
"payoutCents": 0,
"clearingPriceCents": null,
"winningTarget": null,
"targetsSolicited": 0,
"bidsReceived": 0,
"elapsedMs": 19,
"statusCode": 200
}| Column | What it shows |
|---|---|
| Auction | The public reference, such as AU-100483. It is what you and the publisher both quote. |
| Publisher | Who asked. |
| Campaign | Which campaign they asked about. The key decides it, so it is never in the request. |
| Caller | Masked to the last four digits unless the campaign discloses caller ID. |
| Answer | The outcome — see the table below. |
| Why | For anything but a win, the reject code and a sentence written for the person reading it. |
| Payout offered | What you quoted the publisher, when you bid. |
| Took | Milliseconds, from the request arriving to the answer leaving. |
| Outcome | Means |
|---|---|
won | You bid, and the reservation was taken. |
no_bid | You did not bid. The reason is on the row. |
cached | The publisher repeated a request they already held a live bid for, and were given the same bid rather than a second reservation. |
rate_limited | Over the key's limit. Answered 429. |
blocked | Refused before scoring: a suppressed caller, or an identical request already in flight. |
error | Something on this side failed. Worth raising. |
What an outbound row holds
An outbound row records one ask of one buyer: the request as it was sent — after macros were filled in — the buyer's raw answer, the HTTP status, the latency, what your response paths read out of it, and whether that bid went on to win.
| Status | Means |
|---|---|
bid | They priced the call. |
no_bid · rejected | They declined it, with whatever reason they gave. |
timeout | They did not answer inside the campaign's ping budget and were excluded from that auction. |
error | Their endpoint failed, or answered something unreadable. |
won · claimed | Their bid won, and the caller was sent to them. |
lost | Another target took the call. The loss code says why. |
expired | The bid stood and the publisher never sent the caller. |
void | The caller went away before delivery. |
Reading one ping
Opening an inbound ping shows its trace — the same idea as a call's waterfall, for the auction that priced it. The page is laid out as When and Took, then What happened, then Details, the Tags the publisher sent, The call this became, and finally the two bodies.
- What happened is the outcome in words: whether a cached bid was reused, which filters ran, which targets were candidates and why the others were skipped, which buyers were pinged and which timed out, how the bids ranked, and the reservation taken for the winner.
- Details carries the publisher's own reference, their id for the call, the address the request came from, the user agent, whether they said they could take SIP, and the quote's expiry.
- Tags the publisher sent is labelled unverified on the page, because a publisher can put anything in a ping. Treat it as a claim, not a fact.
- What the publisher sent and What we answered are the two bodies exactly as they crossed the wire — which is the point of the page. A tidied version would make the one argument this log exists to settle unwinnable.
- A won ping links to the call it became, once the caller arrives.
The summary
The same window, rolled up — the screen for "why is my fill rate what it is" rather than "what happened to that ping".
curl "https://api.buy3.io/api/tracking/pings/summary?from=2026-09-19T05:00:00Z&to=2026-09-20T05:00:00Z" \
-H "Authorization: Bearer $BUY3_SESSION_TOKEN"| Section | What it answers |
|---|---|
| Inbound | Pings received, how many you bid on, the win rate, how fast we answer as a median and a 95th percentile, and the payout offered. A mean would hide exactly the slow tail that loses auctions. |
| No-bid reasons | Every refusal ranked, with its share and its sentence. The top row is usually the whole story. |
| Targets | Per buyer endpoint: pings, bids, declines, timeouts, errors, median and 95th-percentile latency, and the average bid. This is the table that names the buyer costing every auction its budget. |
| Publishers | Per publisher: pings, wins, and how many of those wins became a call. |
The outbound summary answers the mirror-image question: pings sent, how many came back with a bid, how many won the call, and how many timed out or errored. A buyer who times out on a fifth of your auctions is not a buyer who bids low; they are a buyer who is not in the auction at all.
Filters, windows and retention
- Filter by campaign, publisher, target, outcome or status, and by reject code — which is the fastest way from "our fill rate dropped" to the reason.
- The window defaults to the last seven days, in the workspace's own days, and may span at most 92.
- Rows are pruned after thirty days. Asking for a wider window is allowed and simply returns nothing older than that, so export anything you need to keep.
- Calls keep the bid they were won on, so a call's own record outlives the ping that produced it.
Exporting
curl -L "https://api.buy3.io/api/tracking/pings/export.csv?direction=inbound&from=2026-09-19T05:00:00Z" \
-H "Authorization: Bearer $BUY3_SESSION_TOKEN" \
-o pings-inbound.csvThe two directions export separately, because they have different columns and a file with half its cells empty is two files pretending. The inbound file carries the reference, the outcome, the reject code with its reason and sentence, the campaign and publisher, the caller and state, the latency and status, the payout offered and the clearing price, the winning target, and how many targets were asked and how many answered. The outbound file carries the bid, the auction it belonged to, the target and buyer, the status, the bid, the HTTP status and latency, the reject code and sentence, and the call it became.
Every export is written to the activity log before the first byte, with the direction, the filters, the window and the row count. The encoding, quoting and cap are the same as every other file the platform writes — see the file.
Who can read what
You read every ping in your workspace, in both directions. Each publisher, in the publisher portal, reads the pings they sent: their request, your answer and the reason, with a sentence and whose move it is. They never see your buyers' bids, the auction's trace, your margin or anybody else's traffic.
A worked example
Fill on Acme Health inbound drops from about a quarter to under a tenth over a weekend.
- Open the summary for the last three days, inbound. Pings received are flat; the win rate has halved.
- Read the no-bid reasons.
1003 no_capacityhas gone from a tenth of refusals to over half. That is a demand problem, not a supply one. - Switch to the targets table. One buyer's timeout rate has gone from 2 per cent to 34, with a 95th-percentile latency far over the campaign's budget. They are being excluded from most auctions.
- Open one outbound row on that target. The request is exactly what it always was; the answer is empty and the latency sits on the budget. It is their endpoint, not your template.
- Export the inbound ledger for the window and send the buyer their own numbers alongside the dates.
- Meanwhile, check the publishers table. Northwind's won-to-call rate is 96 per cent — they are not the problem, and telling them so is worth as much as fixing the buyer.
