---
title: "Start a collection"
method: POST
path: "/collections"
tags: ["Collections"]
---

# Start a collection

`POST /collections`

## Request

- Base URL: `https://api.gmaps.dev/v1`
- URL: `https://api.gmaps.dev/v1/collections`
- Auth: HTTP bearer

Search a whole area, square by square, and collect up to maxPlaces of the places in it. Answers straight away with the collection, then works through the area in the background. Read the collection again to see how far it has got. Starting one costs nothing: each new place costs one credit as it arrives, and saved places cost nothing. Needs an API key.

## Headers

- `Idempotency-Key` string

## Request body

- object
  - `keywords` string[], required: What to search for, such as ["bakery", "coffee shop"]. One search per term.
  - `geo` union, required: Where to search. Give exactly one of: a Brazilian city as location, a lat/lng point with radiusM, a bbox, or a cityIbge with radiusKm.
    - object
      - `location` string, required
    - object
      - `lat` number, required
      - `lng` number, required
      - `radiusM` number, required
    - object
    - object
      - `cityIbge` integer, required
      - `radiusKm` number, required
  - `lang` string: The language to search in. New places come back in it. Two letters, such as pt or en.
  - `minRating` number, nullable: Keep only places rated at least this, from 1 to 5. Null keeps every place.
  - `maxPlaces` integer: Stop after this many places. Defaults to the most your plan collects at once.
  - `enrich` string[]: Add ["emails"] to also look for email addresses. Costs one more credit for each place that has one, and needs a paid plan.

## Response `202`

Start a collection

- object
  - `data` object, required
    - `id` string, uuid, required: Our id for the collection. get_collection, list_collection_places, cancel_collection and create_export take it.
    - `kind` 'search' | 'collection', required: `collection` when you asked for it, `search` when a search started it to fill its `limit`.
    - `status` 'queued' | 'running' | 'succeeded' | 'partial' | 'failed' | 'cancelled', required: `queued` while it waits its turn, `running` while it searches, then `succeeded`, `partial`, `failed` or `cancelled`.
    - `stopReason` 'quota' | 'daily_cap' | 'cancelled' | 'engine' | 'empty', nullable, required: Why it stopped short, or found nothing. A credit limit shows here as soon as it is hit, while the search already in flight finishes. Null when it searched the whole area or reached `maxPlaces`.
    - `query` object, required: What you asked for, exactly as it ran, with `maxPlaces` filled in from your plan when you left it out.
      - `keywords` string[], required: What it searched for. One search per term.
      - `geo` union, required: The area, as you gave it: a Brazilian city as location, a lat/lng point with radiusM, a bbox, or a cityIbge with radiusKm.
        - object
          - `location` string, required
        - object
          - `lat` number, required
          - `lng` number, required
          - `radiusM` number, required
        - object
        - object
          - `cityIbge` integer, required
          - `radiusKm` number, required
      - `lang` string, required: The language it searched in. Two letters, such as pt or en.
      - `minRating` number, nullable, required: Only places rated at least this were kept, from 1 to 5. Null when every place was.
      - `maxPlaces` integer, required: It stops after this many places: what you asked for, or the most your plan collects at once.
      - `enrich` string[], required: ["emails"] when it also looks for email addresses, which costs one more credit for each place that has one. Empty when it doesn't.
    - `area` union, required: Where it searched, ready to draw: a lat/lng centre with radiusM in metres, or a bbox as [west, south, east, north]. A city is the circle we searched around its centre. Null for an old collection whose city we can no longer place.
      - object
        - `lat` number, required
        - `lng` number, required
        - `radiusM` integer, required
      - object
    - `squaresTotal` integer, required: How many searches the area takes: one per square of the area, for each term.
    - `squaresCovered` integer, required: How many of those we searched recently enough to reuse. Their places cost nothing.
    - `jobsTotal` integer, required: Batches of searching. Each one covers several squares around one point.
    - `jobsDone` integer, required: How many of those batches have finished.
    - `counts` object, required: What it has delivered so far, and what that cost.
      - `placesDelivered` integer, required: Places handed over so far: always `placesFresh` plus `placesCached`.
      - `placesFresh` integer, required: Places we collected for you just now, either new to us or too old to reuse. Each costs one credit.
      - `placesCached` integer, required: Places we already had saved. They cost nothing.
      - `creditsCharged` integer, required: Credits taken: one per fresh place, plus one per place we found an email for.
      - `creditsRefunded` integer, required: Credits given back. A collection that delivers nothing refunds all of them.
    - `error` object, nullable, required: Why it failed, shaped like any other error. Null unless `status` is `failed`.
      - `code` 'VALIDATION_ERROR' | 'UNAUTHORIZED' | 'INVALID_TOKEN' | 'PLAN_GATE' | 'AUP_REQUIRED' | 'FORBIDDEN' | 'NOT_FOUND' | 'PLACE_NOT_FOUND' | 'COLLECTION_NOT_FOUND' | 'EXPORT_NOT_FOUND' | 'CONFLICT' | 'PAYLOAD_TOO_LARGE' | 'GEO_UNRESOLVED' | 'UPSTREAM_BLOCKED' | 'RATE_LIMIT_EXCEEDED' | 'QUOTA_EXCEEDED' | 'INTERNAL_ERROR' | 'ENGINE_UNAVAILABLE' | 'SERVICE_UNAVAILABLE' | 'BILLING_UNAVAILABLE', required: What went wrong, as a word a program can check.
      - `message` string, required: What went wrong, as a sentence.
      - `messageKey` 'serverErrors.validation' | 'serverErrors.unauthorized' | 'serverErrors.keyInactive' | 'serverErrors.signInRequired' | 'serverErrors.planGateEmails' | 'serverErrors.planGateWebhooks' | 'serverErrors.planGateContact' | 'serverErrors.aupRequired' | 'serverErrors.forbidden' | 'serverErrors.accountSuspended' | 'serverErrors.accountDeletionRequested' | 'serverErrors.accountDeleted' | 'serverErrors.lastStaff' | 'serverErrors.notFound' | 'serverErrors.placeNotFound' | 'serverErrors.placeShortLink' | 'serverErrors.placeLinkNotOne' | 'serverErrors.placeRefUnreadable' | 'serverErrors.collectionNotFound' | 'serverErrors.collectionEnded' | 'serverErrors.collectionPlacesDeleted' | 'serverErrors.exportNotFound' | 'serverErrors.exportNothingYet' | 'serverErrors.exportNothingFound' | 'serverErrors.conflict' | 'serverErrors.payloadTooLarge' | 'serverErrors.geoUnresolved' | 'serverErrors.cityAmbiguous' | 'serverErrors.cityDidYouMean' | 'serverErrors.cityUnknown' | 'serverErrors.cityIbgeUnknown' | 'serverErrors.areaTooLarge' | 'serverErrors.radiusTooLarge' | 'serverErrors.noSearchTerms' | 'serverErrors.tooManyTerms' | 'serverErrors.maxPlacesOverPlan' | 'serverErrors.xlsxTooLarge' | 'serverErrors.keyLimit' | 'serverErrors.subscriptionOpen' | 'serverErrors.checkoutOpen' | 'serverErrors.webhookNotHttps' | 'serverErrors.webhookCredentials' | 'serverErrors.webhookPrivateAddress' | 'serverErrors.webhookNoAddress' | 'serverErrors.webhookDuplicate' | 'serverErrors.webhookUnreachable' | 'serverErrors.webhookTimeout' | 'serverErrors.webhookStatus' | 'serverErrors.webhookRedirect' | 'serverErrors.webhookStopped' | 'serverErrors.webhookOff' | 'serverErrors.upstreamBlocked' | 'serverErrors.rateLimit' | 'serverErrors.quotaMonth' | 'serverErrors.quotaDay' | 'serverErrors.concurrency' | 'serverErrors.internal' | 'serverErrors.engineUnavailable' | 'serverErrors.collectionEngineFailed' | 'serverErrors.serviceUnavailable' | 'serverErrors.billingUnavailable' | 'serverErrors.billingCurrencyUnavailable': `message` as a key, to show it in your reader's language, with `params` filling its blanks.
      - `params` object: The values that fill `messageKey`'s blanks, such as a limit.
      - `details` unknown
    - `queuedAt` string, date-time, required: When we accepted it. ISO 8601 with an offset, as every date here is.
    - `startedAt` string, date-time, nullable, required: When the first search started. Null while it waits its turn.
    - `finishedAt` string, date-time, nullable, required: When it ended. Null while it runs.
    - `cancelRequestedAt` string, date-time, nullable, required: Set the moment you cancel. The search already in flight still finishes.
    - `resultsExpireAt` string, date-time, required: After this, the counts stay but its places can no longer be listed. Export them first.
  - `meta` union
    - object
      - `total` integer, required: Every item that matches, not just this page.
      - `limit` integer, required: The most items a page holds.
      - `offset` integer, required: How many items came before this page.
      - `hasMore` boolean, required: True when another page follows.
    - object
      - `cost` integer, required: Credits this call took.
      - `creditsRemaining` integer, required: Credits left after this call, this month's and bought ones together.
      - `requestId` string, required: This call's id. Send it to support if something went wrong.
      - `cached` boolean: True when this response used only saved results.

## Other responses

- `default`: Any other failure.

---

[API](https://skmtc.dev/gmaps/apis/gmaps-dev.md) · [All operations](https://skmtc.dev/gmaps/apis/gmaps-dev/llms.txt) · [OpenAPI document](https://skmtc.dev/gmaps/apis/gmaps-dev/revisions/0b3459aaf802?raw)
