CrawlHawk API

Crawl any website, extract products, contacts or clean markdown — from your own code. Job-based and asynchronous: start a job, poll or receive a webhook, download the results.

https://api.crawlhawk.com/v1

Authentication

Every request carries your API key as a Bearer token. Create keys on your Account → API page.

curl https://api.crawlhawk.com/v1/account \
  -H "Authorization: Bearer chk_live_…"
Keys are shown once at creation and stored hashed. A leaked key can spend your credits — revoke it immediately on the Account → API page.

POST/v1/estimate

Returns the per-unit rate, whether the site needs the protected tier, and a page estimate read from the sitemap when there is one. Starts nothing and spends no credits.

Body

FieldTypeDescription
urlREQUIREDstring
modeREQUIRED"links" · "sitemap" · "products" · "markdown" · "contacts"
domain_countinteger`contacts` only: how many domains the run would cover, when you already know the list size.
curl -X POST https://api.crawlhawk.com/v1/estimate \
  -H "Authorization: Bearer chk_live_…" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://example.com","mode":"links"}'

Response 200

{
  "url": "string",
  "mode": "links",
  "credit_per_unit": 0,
  "credit_per_1000": 0,
  "protected": false,
  "requires_protected_tier": false,
  "protected_marker": "string",
  "estimated_pages": 0,
  "estimated_credits": 0,
  "credits_available": 0
}

GET/v1/jobs

List the account's runs

curl https://api.crawlhawk.com/v1/jobs \
  -H "Authorization: Bearer chk_live_…"

Response 200

{
  "items": [
    {
      "job_id": "string",
      "mode": "links",
      "status": "queued",
      "url": "string",
      "counts": {},
      "credits_used": 0,
      "credit_per_unit": 0,
      "protected": false,
      "batch": 0,
      "batch_ready": false,
      "next_batch": 0,
      "external": {},
      "source": "string",
      "created_at": "2026-08-21T10:00:00Z",
      "finished_at": "2026-08-21T10:00:00Z",
      "error": {}
    }
  ],
  "total": 0,
  "page": 0,
  "page_size": 0
}

POST/v1/jobs

Start a run

Parameters

FieldInTypeDescription
Idempotency-KeyheaderstringSend a unique value per intended job. A retry with the same key and body replays the original response for 24 hours instead of starting — and charging for — a second run.

Body

FieldTypeDescription
urlstringStart URL. Required for every mode except `contacts`, where `domains` may be used instead.
modeREQUIRED"links" · "sitemap" · "products" · "markdown" · "contacts"`links` (URL inventory), `sitemap` (XML sitemap), `products` (structured product data), `markdown` (page text for RAG), `contacts` (company/person contact data).
domainsstring[]`contacts` only: the domains to process.
scopeobject
optionsobject
on_protected"auto" · "pause" · "fail"What to do when the site turns out to be bot-protected and the rate jumps. `auto`: continue while the rate stays at or under `max_credit_per_unit`. `pause`: stop at `awaiting_confirmation` and wait for POST /v1/jobs/{id}/confirm. `fail`: end the run rather than pay more.
max_credit_per_unitintegerCredit ceiling per URL that `on_protected: auto` may accept without asking. Required when `on_protected` is `auto`.
legal_basis_acceptedboolean`contacts` only, and mandatory there: the caller confirms a lawful basis for processing the contact data this run collects (Terms 5.7). The acceptance is recorded against the key owner and the call time.
externalobject
curl -X POST https://api.crawlhawk.com/v1/jobs \
  -H "Authorization: Bearer chk_live_…" \
  -H "Content-Type: application/json" \
  -d '{"mode":"links"}'

Response 202

{
  "job_id": "string",
  "status": "queued",
  "mode": "links",
  "url": "string",
  "created_at": "2026-08-21T10:00:00Z"
}

GET/v1/jobs/{id}

Read a run’s status

Parameters

FieldInTypeDescription
idREQUIREDpathstring
curl https://api.crawlhawk.com/v1/jobs/583d8118-… \
  -H "Authorization: Bearer chk_live_…"

Response 200

{
  "job_id": "string",
  "mode": "links",
  "status": "queued",
  "url": "string",
  "counts": {
    "pages": 0,
    "products": 0,
    "results": 0
  },
  "credits_used": 0,
  "credit_per_unit": 0,
  "protected": false,
  "batch": 0,
  "batch_ready": false,
  "next_batch": 0,
  "external": {},
  "source": "string",
  "created_at": "2026-08-21T10:00:00Z",
  "finished_at": "2026-08-21T10:00:00Z",
  "error": {}
}

POST/v1/jobs/{id}/confirm

Resolves both machine-side stops: a protected-tier escalation waiting on `awaiting_confirmation`, and a run parked on a batch boundary (`{"continue_batch": true}`).

Parameters

FieldInTypeDescription
idREQUIREDpathstring

Body

FieldTypeDescription
continue_batchbooleanContinue past a batch boundary (`batch.ready` is true) instead of confirming a protected-tier escalation.
max_credit_per_unitintegerRaise the run's per-URL credit ceiling as part of confirming, so a later escalation does not stop it again.
curl -X POST https://api.crawlhawk.com/v1/jobs/583d8118-…/confirm \
  -H "Authorization: Bearer chk_live_…"

Response 200

{
  "job_id": "string",
  "mode": "links",
  "status": "queued",
  "url": "string",
  "counts": {
    "pages": 0,
    "products": 0,
    "results": 0
  },
  "credits_used": 0,
  "credit_per_unit": 0,
  "protected": false,
  "batch": 0,
  "batch_ready": false,
  "next_batch": 0,
  "external": {},
  "source": "string",
  "created_at": "2026-08-21T10:00:00Z",
  "finished_at": "2026-08-21T10:00:00Z",
  "error": {}
}

POST/v1/jobs/{id}/abort

Stop a run

Parameters

FieldInTypeDescription
idREQUIREDpathstring
curl -X POST https://api.crawlhawk.com/v1/jobs/583d8118-…/abort \
  -H "Authorization: Bearer chk_live_…"

Response 200

{
  "job_id": "string",
  "mode": "links",
  "status": "queued",
  "url": "string",
  "counts": {
    "pages": 0,
    "products": 0,
    "results": 0
  },
  "credits_used": 0,
  "credit_per_unit": 0,
  "protected": false,
  "batch": 0,
  "batch_ready": false,
  "next_batch": 0,
  "external": {},
  "source": "string",
  "created_at": "2026-08-21T10:00:00Z",
  "finished_at": "2026-08-21T10:00:00Z",
  "error": {}
}

GET/v1/jobs/{id}/results

The shape follows the mode. This is the endpoint an agent should read; `export` exists for handing a file to a human.

Parameters

FieldInTypeDescription
idREQUIREDpathstring
curl https://api.crawlhawk.com/v1/jobs/583d8118-…/results \
  -H "Authorization: Bearer chk_live_…"

Response 200

{
  "job_id": "string",
  "mode": "string",
  "items": [
    {}
  ],
  "total": 0,
  "page": 0,
  "page_size": 0,
  "has_more": false
}

POST/v1/jobs/{id}/export

Returns a signed, single-use URL. The URL carries no API key, so it can be passed on to whatever needs the file.

Parameters

FieldInTypeDescription
idREQUIREDpathstring

Body

FieldTypeDescription
formatREQUIRED"xlsx" · "csv" · "json" · "md" · "xml"
curl -X POST https://api.crawlhawk.com/v1/jobs/583d8118-…/export \
  -H "Authorization: Bearer chk_live_…" \
  -H "Content-Type: application/json" \
  -d '{"format":"xlsx"}'

Response 200

{
  "url": "string",
  "format": "xlsx",
  "expires_at": "2026-08-21T10:00:00Z"
}

GET/v1/account

Credit balance, key metadata and the documented ceilings

curl https://api.crawlhawk.com/v1/account \
  -H "Authorization: Bearer chk_live_…"

Response 200

{
  "account_id": "string",
  "credits": 0,
  "top_up_url": "string",
  "key": {},
  "auth_method": "api_key",
  "scopes": [
    "string"
  ],
  "keys_active": 0,
  "webhook_failing": false,
  "limits": {}
}

GET/v1/webhooks

List the account’s webhook endpoints

curl https://api.crawlhawk.com/v1/webhooks \
  -H "Authorization: Bearer chk_live_…"

Response 200

[
  {
    "id": "string",
    "url": "string",
    "events": [
      "job.completed"
    ],
    "active": false,
    "created_at": "2026-08-21T10:00:00Z",
    "last_success_at": "2026-08-21T10:00:00Z",
    "failing_since": "2026-08-21T10:00:00Z",
    "last_error": "string"
  }
]

POST/v1/webhooks

The signing secret is returned once, here. Every delivery carries an X-CrawlHawk-Signature of `t=<unix>,v1=<hmac-sha256 of "t.body">` computed with it.

Body

FieldTypeDescription
urlREQUIREDstringHTTPS endpoint to deliver to. Private and loopback addresses are refused.
events"job.completed" · "job.failed" · "job.awaiting_confirmation" · "job.batch_ready"[]Events to subscribe to. Every event when omitted.
curl -X POST https://api.crawlhawk.com/v1/webhooks \
  -H "Authorization: Bearer chk_live_…" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://example.com"}'

Response 201

{
  "id": "string",
  "url": "string",
  "events": [
    "job.completed"
  ],
  "active": false,
  "created_at": "2026-08-21T10:00:00Z",
  "last_success_at": "2026-08-21T10:00:00Z",
  "failing_since": "2026-08-21T10:00:00Z",
  "last_error": "string",
  "secret": "string"
}

DELETE/v1/webhooks/{id}

Remove a webhook endpoint

Parameters

FieldInTypeDescription
idREQUIREDpathstring
curl -X DELETE https://api.crawlhawk.com/v1/webhooks/583d8118-… \
  -H "Authorization: Bearer chk_live_…"

Webhooks

Crawls take minutes, so register a URL on your account instead of polling. You receive job.completed · job.failed · job.awaiting_confirmation · job.batch_ready events, each HMAC-signed with your webhook secret in the X-CrawlHawk-Signature header. Failed deliveries are retried with exponential backoff.

Errors

Errors are structured and machine-readable. The code is stable; messages may change.

HTTPcodeMeaning
402insufficient_creditsBody carries required, available and top_up_url
422legal_basis_requiredcontacts mode without acceptance
409awaiting_confirmationThe job is waiting for /confirm
429rate_limitedInfrastructure ceiling — retry with backoff

Billing & auto top-up

Calls spend the credits on the account the key belongs to. Credits never expire and there is no subscription. When a job would cost more than the balance covers, the API answers 402 with the exact required and available amounts and a top_up_url — so an integration can surface the number rather than fail silently.

Balance and the published ceilings are readable at any time from GET /v1/account.