Developers

API documentation

A small REST API: start a scrape, poll it, read the rows. Everything the dashboard does, your code can do. Available on every plan, including the free 100 credits — no separate API tier, no sales call.

Authentication

Generate a key in Settings. It starts with hub_live_ and is shown once — we only store a hash, so if you lose it you rotate rather than recover it. Pass it as a Bearer token on every request.

curl https://hubertino.com/api/v1/scrapes \
  -H "Authorization: Bearer hub_live_your_key_here"

Base URL: https://hubertino.com/api/v1. All request and response bodies are JSON. Missing or invalid keys return 401.

Credits & billing

One credit is one business delivered. When a scrape starts we reserve its worst case (categories × locations × maxResults, capped at your balance); when it finishes you're charged only for rows actually delivered and the rest is refunded automatically. A search that finds nothing costs nothing.

The reservation is why a job can report creditsReserved much larger than creditsCharged— that's expected, not a bug.

Quickstart

Start a scrape, poll until it's done, then read the rows.

# 1. start
ID=$(curl -s https://hubertino.com/api/v1/scrapes \
  -H "Authorization: Bearer $HUBERTINO_KEY" \
  -H "content-type: application/json" \
  -d '{"categories":["dentist"],"locations":["Austin, TX"],"maxResults":50}' \
  | jq -r .scrape.id)

# 2. poll — status goes queued -> running -> done
curl -s "https://hubertino.com/api/v1/scrapes/$ID" \
  -H "Authorization: Bearer $HUBERTINO_KEY" | jq .scrape.status

# 3. read rows, a page at a time
curl -s "https://hubertino.com/api/v1/scrapes/$ID/results?limit=1000&offset=0" \
  -H "Authorization: Bearer $HUBERTINO_KEY" | jq '.count, .hasMore'

Results are readable while the job is still running — rows stream in as they're found, and partial tells you whether more are coming.

Start a scrape

POST/api/v1/scrapes
FieldTypeNotes
categoriesstring[]Required. What to search for — “dentist”, “HVAC contractor”. Up to 1,000.
locationsstring[]Required. Cities, regions or zip codes. Up to 10,000.
maxResultsintegerRequired. Per category × location pair, 1–500.
countrystringOptional ISO code picking the Google edition. Inferred from the locations when omitted.
enrichWebsitebooleanDefault true. Visits each business's site for an email. No extra credit cost.
enrichLinksbooleanDefault true. Collects social profiles and ordering links.

One scrape runs up to 2,000 searches (categories × locations) — split anything larger into several scrapes. Returns 201 with the created scrape.

{
  "scrape": {
    "id": "6f1c…", "kind": "scrape", "status": "queued",
    "categories": ["dentist"], "locations": ["Austin, TX"],
    "maxResults": 50, "creditsReserved": 50, "creditsCharged": 0,
    "resultCount": 0, "createdAt": "2026-07-22T18:04:11.000Z"
  }
}

List scrapes

GET/api/v1/scrapes

Your 200 most recent scrapes, newest first, as { "scrapes": [...] }.

Get one scrape

GET/api/v1/scrapes/{id}

Returns the scrape with live status. status is one of queued, running, done, error or cancelled. Poll every few seconds; there are no webhooks yet.

Fetch results

GET/api/v1/scrapes/{id}/results

Paginated. limit defaults to 1,000 and caps at 5,000; offset defaults to 0. Page until hasMore is false.

{
  "status": "done",
  "partial": false,
  "count": 4820,          // total available, not this page
  "offset": 0,
  "limit": 1000,
  "hasMore": true,
  "columns": ["name", "phone", "email", "website", …],
  "rows": [ { "name": "…", "phone": "…", "email": "…" }, … ]
}

Each row carries a _status of found extracted enriched, so you can tell a fully-detailed row from one still being filled in. When a job is capped at your credit allowance, detailed rows are delivered first.

Export XLSX or CSV

GET/api/v1/scrapes/{id}/export?format=xlsx

Returns the file itself, not JSON — format is xlsx (default) or csv. Only available once the scrape is done; earlier calls return 409.

curl -L "https://hubertino.com/api/v1/scrapes/$ID/export?format=csv" \
  -H "Authorization: Bearer $HUBERTINO_KEY" -o leads.csv

Rate limits

Creating scrapes is limited to 12 per 5 minutes per account; exceeding it returns 429. Reads (status, results, export) aren't rate limited, but poll at a sensible interval — a few seconds is plenty, since scrapes take minutes.

Errors

Errors are JSON with an error string and a meaningful status code.

400Invalid body or query parameter — the message says which field.
401Missing or invalid API key.
402Not enough credits to reserve the scrape.
404No such scrape under your account.
409Not ready yet — e.g. exporting a scrape that hasn't finished.
410Results are no longer stored for this scrape.
429Rate limit exceeded.
503Results temporarily unavailable — retry.

Get an API key

Start free with 100 credits — no card. The API is on every plan.

Start free with 100 credits