Skip to main content

Documentation Index

Fetch the complete documentation index at: https://brezelscraper.com/docs/llms.txt

Use this file to discover all available pages before exploring further.

Job statuses

StatusMeaning
pendingThe job has been accepted and is waiting to run
runningThe scraper is actively collecting data
completedThe job finished successfully
failedThe job did not complete successfully
cancelledThe job was cancelled
abortingCancellation is in progress

POST /api/v1/jobs

Create a new scraping job.

Request body

FieldTypeRequiredDefaultNotes
namestringYesnone1 to 200 characters
keywordsstring[]Yesnone1 to 5 keywords, each 1 to 200 characters
langstringYesnone2-character language code
depthintegerNo5Min 1, max 20
emailbooleanNofalseEnables email scraping from business websites
images_maxintegerNo0Total image cap for the whole job, max 40000
reviews_maxintegerNo0Review cap per place, max 500
max_resultsintegerNo50Place cap for the whole job, max 500
latstringNoemptyLatitude for geo-targeted runs
lonstringNoemptyLongitude for geo-targeted runs
zoomintegerNo0Map zoom, max 21
radiusintegerNo0Meters, max 50000
max_timeintegerNo1800Seconds, max 3600
fast_modebooleanNofalseFaster, lighter data collection
proxiesstring[]NoemptyUp to 100 custom proxy URLs
There is no unlimited sentinel for cap fields. If you want the maximum allowed value, send the maximum explicitly.

Supported lang values

ar, bg, cs, da, de, el, en, es, et, fi, fr, he, hr, hu, id, it, ja, ko, lt, lv, ms, nl, no, pl, pt, ro, ru, sk, sl, sv, th, tr, uk, vi, zh

Example request

curl -X POST https://api.brezelscraper.com/api/v1/jobs \
  -H "Authorization: Bearer bscraper_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Berlin cafes",
    "keywords": ["cafes in berlin"],
    "lang": "en",
    "depth": 5,
    "max_results": 100,
    "reviews_max": 0,
    "images_max": 0,
    "max_time": 1800
  }'

Success response

{
  "id": "d8d8a24e-cef2-4b02-8396-abc290b6f299"
}

Common failure modes

  • 400 Bad Request for validation failures
  • 402 Payment Required if your credit balance is too low
  • 409 Conflict if you reuse an idempotency key incorrectly
  • 422 Unprocessable Entity for invalid JSON
  • 429 Too Many Requests if you hit the concurrent job limit, with Retry-After: 60

POST /api/v1/jobs/estimates

Check how much a scraping job will cost before you create it. Use this endpoint to show users a price preview in your UI, or to decide programmatically whether to proceed with a job based on cost and available credits.

Why use this endpoint

  • You get a cost breakdown before spending any credits.
  • The response includes your current credit balance so you know if you can afford the job.
  • The estimate shows a range (min to max) because Google Maps results vary by location and search term.

Request body

ParameterTypeRequiredDefaultDescription
keywordsstring[]YesThe search terms for your job. 1 to 5 keywords, each up to 200 characters. More keywords means more places found and higher cost.
depthintegerNo5How far to scroll in Google Maps search results. Higher depth finds more places per keyword. Range: 1 to 20.
include_emailsbooleanNofalseWhether to extract emails from business websites. Adds a per-place cost.
max_imagesintegerNo0Total number of images to collect across all places. 0 means skip images. Maximum 40000.
max_reviewsintegerNo(uses average)Maximum reviews to collect per place. 0 means skip reviews. If omitted, the estimate uses a realistic average (~50 per place). Range: 0 to 500.
max_resultsintegerNo(uses depth)Hard cap on total places returned. If omitted, the estimate is based on depth and keyword count. Range: 1 to 500.
Language is not needed here because it does not affect cost.

Example request

{
  "keywords": ["Cafe Mitte Berlin"],
  "depth": 5,
  "include_emails": false,
  "max_reviews": 0,
  "max_images": 0
}

Example response

{
  "estimate": {
    "object": "job_estimate",
    "currency": "credits",
    "places": 40,
    "places_min": 20,
    "places_max": 48,
    "keyword_count": 1,
    "places_per_keyword": 40,
    "total": 0.127,
    "total_min": 0.067,
    "total_max": 0.151,
    "breakdown": {
      "job_start_cost": 0.007,
      "places_cost": 0.12,
      "contact_details_cost": 0,
      "reviews_cost": 0,
      "images_cost": 0
    },
    "reviews": 0,
    "images": 0,
    "includes_emails": false,
    "unit_prices": {
      "job_start": 0.007,
      "place_scraped": 0.003,
      "contact_details": 0.002,
      "review": 0.0005,
      "image": 0.0005
    },
    "max_results_provided": false,
    "description": "Estimated ~40 places for 1 keyword at depth 5 (range: 20 to 48).",
    "expires_at": 1713110460
  },
  "balance": {
    "current": 24.99,
    "sufficient": true
  }
}

Response fields

estimate object containing the cost calculation:
FieldTypeDescription
objectstringAlways "job_estimate".
currencystringAlways "credits". One credit equals one US dollar.
placesintegerHow many places the job will likely scrape. This is the primary estimate used for pricing.
places_minintegerConservative lower bound on places. Sparse markets (few results for your search term) may produce this many.
places_maxintegerUpper bound on places. Dense markets (many results) may produce this many.
keyword_countintegerNumber of keywords in your request.
places_per_keywordintegerEstimated places per keyword based on the depth setting.
totalnumberEstimated total cost in credits for the primary estimate.
total_minnumberLowest possible cost (based on places_min).
total_maxnumberHighest possible cost (based on places_max).
breakdownobjectCost broken down by component: job_start_cost (flat fee per job), places_cost, contact_details_cost (emails), reviews_cost, images_cost.
reviewsintegerEstimated total reviews to be collected.
imagesintegerEstimated total images to be collected.
includes_emailsbooleanWhether email extraction was included in the estimate.
unit_pricesobjectThe per-unit prices used for this estimate. Lets you verify the math yourself.
max_results_providedbooleanWhether you set a max_results cap. If false, the estimate was based on depth.
descriptionstringA human-readable summary of the estimate.
expires_atintegerUnix timestamp. The estimate is accurate until this time. After that, pricing may have changed.
balance object containing your account state:
FieldTypeDescription
currentnumberYour current credit balance.
sufficientbooleanWhether you have enough credits to run this job. Uses the conservative lower bound so you are not blocked by an optimistic estimate.

Notes

  • All costs are in credits. One credit equals one US dollar.
  • The estimate uses your account’s active pricing rules at request time.
  • If you set max_results, it acts as a hard cap. The estimate will not exceed this number of places even if depth would produce more.
  • The Cache-Control: no-store header is set on responses because estimates are ephemeral and should not be cached.

GET /api/v1/jobs

List your jobs with pagination and sorting.

Query parameters

ParameterTypeDefaultNotes
pageinteger1Must be positive
limitinteger10Max 100
sortstringcreated_atAllowed: created_at, name, status, updated_at
orderstringdescasc or desc
searchstringemptyMax 200 bytes

Example response

{
  "jobs": [
    {
      "id": "d8d8a24e-cef2-4b02-8396-abc290b6f299",
      "name": "Berlin cafes",
      "status": "completed",
      "data": {
        "keywords": ["cafes in berlin"],
        "lang": "en",
        "depth": 5,
        "email": false,
        "images_max": 0,
        "reviews_max": 0,
        "max_results": 100,
        "max_time": 1800,
        "fast_mode": false
      },
      "created_at": "2026-04-10T09:00:00Z",
      "updated_at": "2026-04-10T09:05:00Z",
      "source": "api",
      "result_count": 87,
      "total_cost": "12.50"
    }
  ],
  "total": 1,
  "page": 1,
  "limit": 10,
  "has_more": false
}

Response fields

FieldTypeDescription
idstringJob UUID
namestringThe name you set when creating the job
statusstringCurrent status (see Job statuses)
dataobjectThe configuration you submitted. data.max_time is in seconds, matching the request format.
created_atstringWhen the job was created (ISO 8601)
updated_atstringWhen the job status last changed (ISO 8601)
sourcestring"api" if created via API key, "web" if created from the dashboard
result_countintegerNumber of places scraped so far
total_coststringCredits charged for this job (6-decimal string)

GET /api/v1/jobs/{id}

Fetch one job by UUID.
  • 200 OK returns the same job object shape shown above
  • 404 Not Found if the job does not belong to you or does not exist
  • 422 Unprocessable Entity if id is not a valid UUID

POST /api/v1/jobs/{id}/cancel

Cancel a pending or active job. Returns 204 No Content on success.

DELETE /api/v1/jobs/{id}

Delete a job you own. Returns 204 No Content on success. Invalid UUID values return 422 Unprocessable Entity.