Skip to main content
Beta. This endpoint is new and its response shape may still change. Where Export Crawl Analytics returns aggregates, this endpoint returns the individual requests behind them — one row per bot hit, straight from the daily archive.
Exports the raw request log of bot traffic for one domain: every archived crawler request with its timestamp, path, user agent, verification verdict, and response metadata. Three formats:
  • json (default) — rows in a JSON envelope, cursor-paginated.
  • csv — the same rows and pagination as an RFC 4180 CSV download.
  • parquet — presigned download links to the daily Apache Parquet archive files themselves, ready for DuckDB, pandas, or any warehouse. No server-side decoding, no pagination — the fastest way to bulk-load history.
Always call this endpoint from a server-side environment (backend API, serverless function, build script, etc.). Never include your API key in client-side code — it will be visible to anyone inspecting your frontend.
Programmatic (API key) access requires the Pro plan and above — requests from Starter accounts return 403 plan_upgrade_required.
Recent days are provisional. A day is only frozen into the permanent archive after a settling window of a few days; until then it is served from a provisional copy and listed in provisionalDays — real rows, but tier verdicts may still upgrade. See Data freshness. Archived history is retained for ~13 months.

Windows are capped at 31 days

Raw logs are far denser than aggregates, so this endpoint accepts period values of 1d, 7d (the default), or 30d, and explicit startDate/endDate ranges spanning at most 31 days. For longer history, iterate over consecutive windows — or use format: "parquet" and download months of daily files with one request per 31-day window.

Data freshness

Every day in your window falls into exactly one of three states: A finished day becomes exportable shortly after the next UTC midnight. Provisional rows are the same requests you’ll see after the freeze — only the verification verdict (tier) can change, upgrading as late verification completes. If your pipeline depends on final verdicts, re-export any day that appeared in provisionalDays once it has settled.

Request

Headers

Body Parameters

Filters and pageSize apply only to the json/csv formats; sending them with format: "parquet" is a 400 (parquet returns whole archive files, which are never server-filtered).

Example Request

Response

Success (200) — format: "json"

Row Fields

Privacy: no IP addresses. Raw client IPs are never stored anywhere in HadoSEO. The archive keeps only facts derived from the IP before it was dropped, of which the operator/CIDR match above is what this endpoint exposes.

Envelope Fields

Success (200) — format: "csv"

The body is the CSV itself (Content-Type: text/csv), with a header row of the same field names and RFC 4180 quoting. Because there is no JSON envelope, pagination metadata moves to response headers:

Success (200) — format: "parquet"

One file per day (rarely more: extremely high-traffic days split into numbered parts). Files with provisional: true come from the not-yet-frozen copy of a recent day — the frozen file that later replaces it may carry upgraded verdict_tier values. Each url is a temporary, pre-authorized link — fetch it with a plain GET, no Authorization header. The files use the parquet schema underlying the row fields above (snake_case column names: ts as epoch-millisecond INT64, ua_raw, matched_operator, verdict_tier, …) plus two archive-internal columns not present in json/csv rows: stream_id (ingest id) and ip_asn (org-level ASN of the source IP; 0 = unknown).
Treat parquet urls as secrets. Anyone with a link can download that day’s full crawl log until urlExpiresAt — about 1 hour — without authenticating. Don’t log these links or paste them anywhere public; request fresh ones instead.

Pagination

json and csv exports walk the archive oldest → newest and return pageSize rows per request. Pass the returned cursor (body nextCursor, or the X-Next-Cursor header for CSV) as cursor in the next request — by itself; the token pins everything else. Two properties worth knowing:
  • Resuming is cheap: the cursor records an exact file position, so page 50 costs the same as page 2.
  • Consistency is guaranteed: frozen archive files are write-once, and the cursor carries an integrity check — if the file behind your cursor changed you get 400 invalid_cursor rather than silently shifted rows. Restart the export. This is rare for frozen days but expected for provisional days paginated across a day close (they’re rewritten daily), so treat invalid_cursor as a normal restart signal in long-running exports.

Error Responses

Errors return the JSON shapes above regardless of the requested format.

Examples

Which AI bots hit a section, verified only

Bulk-loading history into DuckDB

Each API request counts against your monthly quota. For recurring pipelines, prefer format: "parquet": one request returns links to a full month of daily files, versus potentially many paginated json requests for the same data.