Developer reference

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.

ParameterTypeMeaning
classificationstringOnly 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.
regionstringOnly 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_iduuidOnly rows from one scan. The id is on every scan in your dashboard and on every record as scan_request_id.
date_fromYYYY-MM-DDOnly rows detected on or after this day (UTC).
date_toYYYY-MM-DDOnly rows detected on or before this day (UTC).
formatjson | csvjson (default) returns a paginated JSON page. csv streams every matching row as one file, same columns, no pagination.
pageintegerJSON only. 1-based page number. Defaults to 1.
limitintegerJSON 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.

FieldMeaning
report_idStable id of the detection. Use it to dedupe across pulls.
detected_atWhen the ad was captured (ISO 8601, UTC).
classificationThe verdict on this capture. See the classification values above.
confidence0 to 100 for the verdicts that carry a confidence score; blank for the rest.
keywordThe search term the ad was captured on.
marketTwo-letter market code the capture ran in.
brand_domainYour official domain the search term belongs to.
offender_domainThe domain that placed the ad.
ad_titleHeadline of the captured ad, as served. Treat as untrusted text.
ad_urlDestination URL of the captured ad.
landing_titleTitle of the page the click landed on.
landing_urlFinal URL the click landed on.
affiliate_networkThe affiliate network the route was attributed to, when one was identified.
account_idThe 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_decodedA human-readable form of account_id where the network encodes it. Blank when the id is already plain.
account_provenanceHow 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_hostThe host that carried the account id we are attributing.
offer_idThe network-side offer or program id the route pointed at, when present.
coupon_codesCoupon codes shown on or carried by the capture, separated by semicolons.
scan_request_idThe scan this capture belongs to.
classifier_versionVersion of the detection logic that graded the row. Compare across pulls to see when a verdict was re-graded.
classified_atWhen the verdict was last set (ISO 8601, UTC).
report_urlDirect 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.

StatusMeaning
400A filter is malformed: unknown classification or region, a date that is not YYYY-MM-DD, or a format other than json or csv.
401The key is missing, malformed, unknown, or revoked. The response does not say which.
429The key exceeded its rate limit. Retry-After says how many seconds to wait.
500The read failed on our side. Retry with backoff; nothing is charged.
503Authentication 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 += 1

Versioning

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.