The reports API.
Every detection in your dashboard, as JSON or CSV, from your own scripts and BI tools. One endpoint, read-only, the same records the dashboard export produces.
v1 · updated 2026-09-02
Authentication
Create a key under Settings, API access. The full key is shown once, at creation; we keep only a fingerprint, so a lost key is replaced, not recovered. A key reads the reports of the account that created it and nothing else. There is no write access behind a key anywhere.
Send it on every request as either header:
headers
Authorization: Bearer adc_YOUR_KEY x-api-key: adc_YOUR_KEY
Revoke a key from the same card. Revoked keys answer 401 immediately and stay listed for your records.
Endpoint and filters
request
GET https://adcrime.com/api/v1/reports
All filters are optional and combine with AND.
| Parameter | Type | Meaning |
|---|---|---|
classification | string | Only rows with this verdict. One of: CONFIRMED_AFFILIATE_FRAUD, LIKELY_AFFILIATE_FRAUD, UNKNOWN, CSS_SHOPPING_AD, COMPETITOR_AD, OTHER_ADVERTISER, BRAND_CHANNEL, LEGACY_UNCLASSIFIED, NO_FRAUD, EVIDENCE_CAPTURE_FAILED, EVIDENCE_CAPTURE_INCOMPLETE. Unknown values answer 400. |
region | string | Only rows captured in this market, as the two-letter market code shown in your dashboard (for example us, uk, de). Unknown codes answer 400. |
scan_request_id | uuid | Only rows from one scan. The id is on every scan in your dashboard and on every record as scan_request_id. |
date_from | YYYY-MM-DD | Only rows detected on or after this day (UTC). |
date_to | YYYY-MM-DD | Only rows detected on or before this day (UTC). |
format | json | csv | json (default) returns a paginated JSON page. csv streams every matching row as one file, same columns, no pagination. |
page | integer | JSON only. 1-based page number. Defaults to 1. |
limit | integer | JSON only. Rows per page, 1 to 200. Defaults to 100. |
Response shape
JSON responses carry a page of records under data and a pagination block, newest detection first. total is an estimate for large accounts; walk pages until page reaches totalPages.
200 application/json
{
"data": [
{
"report_id": "6b1f…",
"detected_at": "2026-08-28T10:14:02.000Z",
"classification": "CONFIRMED_AFFILIATE_FRAUD",
"confidence": "96",
"keyword": "yourbrand coupon",
"market": "us",
"brand_domain": "yourbrand.com",
"offender_domain": "coupon-site.example",
"affiliate_network": "Awin",
"account_id": "123456",
"account_provenance": "captured",
"report_url": "https://adcrime.com/dashboard/reports/6b1f…"
}
],
"pagination": { "page": 1, "limit": 100, "total": 412, "totalPages": 5 }
}format=csv streams every matching row as one file with the same columns, oldest first, plus an X-Row-Count header. Cells that begin with =, +, - or @ are prefixed with an apostrophe so spreadsheets read them as text: ad titles come from other people’s pages.
Record fields
Every record has all 23 fields, as strings. Blank means we have nothing defensible to put there, never that the value is zero.
| Field | Meaning |
|---|---|
report_id | Stable id of the detection. Use it to dedupe across pulls. |
detected_at | When the ad was captured (ISO 8601, UTC). |
classification | The verdict on this capture. See the classification values above. |
confidence | 0 to 100 for the verdicts that carry a confidence score; blank for the rest. |
keyword | The search term the ad was captured on. |
market | Two-letter market code the capture ran in. |
brand_domain | Your official domain the search term belongs to. |
offender_domain | The domain that placed the ad. |
ad_title | Headline of the captured ad, as served. Treat as untrusted text. |
ad_url | Destination URL of the captured ad. |
landing_title | Title of the page the click landed on. |
landing_url | Final URL the click landed on. |
affiliate_network | The affiliate network the route was attributed to, when one was identified. |
account_id | The publisher account the capture is attributed to, when one cleared our evidence standard. Blank means we will not name one for this row. |
account_id_decoded | A human-readable form of account_id where the network encodes it. Blank when the id is already plain. |
account_provenance | How account_id was established: captured (observed in the click path) or derived (reconstructed from the affiliate's own landing page). Blank when account_id is blank. |
account_source_host | The host that carried the account id we are attributing. |
offer_id | The network-side offer or program id the route pointed at, when present. |
coupon_codes | Coupon codes shown on or carried by the capture, separated by semicolons. |
scan_request_id | The scan this capture belongs to. |
classifier_version | Version of the detection logic that graded the row. Compare across pulls to see when a verdict was re-graded. |
classified_at | When the verdict was last set (ISO 8601, UTC). |
report_url | Direct link to this capture in your dashboard. |
Rate limits
Each key has 60 units per 60 seconds. A JSON page costs 1 unit; a CSV pull costs 10, because it returns everything at once. Every response tells you where you stand:
headers
X-RateLimit-Limit: … X-RateLimit-Remaining: … X-RateLimit-Reset: …
Over the limit, the request answers 429 with Retry-After in seconds. Nothing is charged for a refused request. If you need more, reply to any AdCrime email and say what you are building.
Errors
Errors are JSON with a single error string written for a person, not a parser. Branch on the status code.
| Status | Meaning |
|---|---|
| 400 | A filter is malformed: unknown classification or region, a date that is not YYYY-MM-DD, or a format other than json or csv. |
| 401 | The key is missing, malformed, unknown, or revoked. The response does not say which. |
| 429 | The key exceeded its rate limit. Retry-After says how many seconds to wait. |
| 500 | The read failed on our side. Retry with backoff; nothing is charged. |
| 503 | Authentication is temporarily unavailable. Retry with backoff. |
Examples
curl: confirmed interceptions, 50 per page
curl "https://adcrime.com/api/v1/reports?classification=CONFIRMED_AFFILIATE_FRAUD&limit=50" \ -H "Authorization: Bearer adc_YOUR_KEY"
curl: everything since 1 Aug as a CSV file
curl "https://adcrime.com/api/v1/reports?format=csv&date_from=2026-08-01" \ -H "Authorization: Bearer adc_YOUR_KEY" \ -o adcrime-reports.csv
python: walk every page
import requests
KEY = "adc_YOUR_KEY"
url = "https://adcrime.com/api/v1/reports"
page = 1
while True:
r = requests.get(url, params={"page": page, "limit": 200},
headers={"Authorization": f"Bearer {KEY}"}, timeout=60)
r.raise_for_status()
body = r.json()
for record in body["data"]:
print(record["detected_at"], record["classification"], record["offender_domain"])
if page >= body["pagination"]["totalPages"]:
break
page += 1Versioning
v1 is stable. We add fields and filters without notice; we never rename or remove a field, change a field’s meaning, or tighten a limit under the same path. Anything that would break a client ships under a new path, and the changelog says so first.