Skip to content

Integrations

TypeScript SDK

Call the API from TypeScript or JavaScript: one typed method per call, plus helpers to watch a collection and check a webhook.

Install it

The package is gmaps-sdk. It has no dependencies of its own.

npm i gmaps-sdk

Create a client

Give it your key, here read from an environment variable. Each method then makes one call and returns what the API answers under data:

import { createClient } from "gmaps-sdk";

const gmaps = createClient({ apiKey: process.env.GMAPS_API_KEY });

const result = await gmaps.places.search({
  keywords: ["bakery"],
  geo: { location: "Curitiba, PR" }
});
console.log(result.places.map((place) => place.identity.name));

What createClient takes:

OptionDefaultWhat it does
apiKey-Your key. Without one, only attributions.list() works.
baseUrlhttps://api.gmaps.dev/v1Where the API is, with /v1 at the end. Change it only to call a local API.
timeout30000How long each try at a call may take, in milliseconds, including reading the answer. A search with wait always gets its whole wait, and a little more.
maxRetries2How many times a failed call is tried again, when that is safe. 0 turns retries off.
fetchglobalThis.fetchYour own fetch, for tests or a runtime that has none.

To give one call a different time limit, or to cancel it, pass { timeoutMs, signal } as its last argument.

const usage = await gmaps.usage.get({ timeoutMs: 5000 });

If a call gets no answer, or an error on our side (a 5xx), the SDK sends it again about 1 second later, and again about 2 seconds after that. It does this only for calls that are safe to run twice: reads, quotes, coverage checks, cancels, and webhook changes and deletes. A search, a new collection, a new export, a new webhook and a webhook test are never sent again this way, because the first try may already have run, and a second could cost credits, send something twice, or make a second export in your list. After a 408, 425 or 429 that asks for a wait of 10 seconds or less, any call waits and goes again.

Every method

Each method takes one object with the same fields as its API call. Methods with no fields, like usage.get(), take only the options.

MethodAPI callWhat it does
places.search(params)POST /v1/places/searchFind places by what they are and where they are. See Search.
places.get({ place })GET /v1/places/{place}Read one place we have saved, by our id, its cid, its place ID or a Maps link. See Places.
collections.quote(params)POST /v1/collections/quoteSee what collecting an area would cost, without starting anything.
collections.create(params)POST /v1/collectionsCollect every place in an area. See Collections.
collections.get({ id })GET /v1/collections/{id}See how far a collection has got and what it has cost.
collections.list(params?)GET /v1/collectionsYour collections, newest first.
collections.places({ id })GET /v1/collections/{id}/placesThe places a collection found, one page at a time.
collections.cancel({ id })DELETE /v1/collections/{id}Stop a collection. You keep the places that already arrived, and the credits they cost stay spent.
watchCollection(id, options?)GET /v1/collections/{id}/streamGet a collection's new places as they arrive. See below.
exports.create(params)POST /v1/exportsGet a link to a collection's places as a csv, json or xlsx file. See Exports.
exports.get({ id })GET /v1/exports/{id}Read an export again while its link still works.
coverage.get(params)POST /v1/coverageSee how much of an area we have searched and how many places we have there. See Coverage.
usage.get()GET /v1/usageCredits used and left, today and this month.
webhooks.create(params)POST /v1/webhooksAdd an endpoint. Its secret comes back this once. See Webhooks.
webhooks.list()GET /v1/webhooksYour endpoints, and whether each one is on.
webhooks.update({ id, … })POST /v1/webhooks/{id}Change an endpoint's events or note, or turn it off and on.
webhooks.test({ id })POST /v1/webhooks/{id}/testSend a test event to an endpoint.
webhooks.deliveries({ id })GET /v1/webhooks/{id}/deliveriesWhat we sent to an endpoint and what it answered.
webhooks.delete({ id })DELETE /v1/webhooks/{id}Remove an endpoint and its delivery log.
attributions.list()GET /v1/attributionsThe open-source projects we build on. Needs no key.

Watch new places arrive

When a search can't fill limit with places we have saved, its answer includes a collection that is finding the rest. watchCollection hands you each new place as it arrives:

const result = await gmaps.places.search({
  keywords: ["bakery"],
  geo: { location: "Curitiba, PR" }
});

if (result.collection) {
  for await (const frame of gmaps.watchCollection(result.collection.id)) {
    if (frame.type === "place") console.log(frame.data);
  }
}
  • Each event has a type and its data. place brings a new place, progress brings the counts so far, and done comes last. Live progress lists every type. A large collection stops sending place events after a set number, so read its places with collections.places once it ends.
  • The loop ends by itself after done. To stop sooner, break out of it.
  • If the connection drops, it connects again and carries on after the last event it gave you, so you never get an event twice or miss one. After too many drops in a row, with no event between them, it throws a GmapsError with NETWORK_ERROR.

What its second argument takes:

OptionDefaultWhat it does
lastEventId-Start after the event with this id, to carry on where you stopped.
signal-An AbortSignal. When it fires, the loop ends quietly, with no error.
retryMs2000How many milliseconds to wait before it connects again.
maxRetries10How many drops in a row it allows before it gives up. Any event, even a heartbeat, starts the count again.

You don't have to watch. You can run the same search again later, when the new places come back for free, or ask the search to wait for them. Search explains both.

When a call fails

Every failed call throws a GmapsError. It carries what the API said, so your code can tell one problem from another:

import { GmapsError } from "gmaps-sdk";

try {
  await gmaps.usage.get();
} catch (error) {
  if (!(error instanceof GmapsError)) throw error;
  console.error(error.status, error.code, error.message);
  console.error(`Request id: ${error.requestId}`);
}
FieldWhat it holds
codeWhat went wrong, such as QUOTA_EXCEEDED. Errors lists every code.
statusThe HTTP status, or 0 when no answer arrived.
messageWhat happened and what to do next, in English.
requestIdOur id for the call. Include it when you write to us.
retryAfterHow many seconds to wait before you try again, when the API says.
detailsMore about the problem, such as scope on a quota error.
messageKeyWith params: a fixed name for the message and its values, so your app can show it in its own language.

Four codes come from the SDK itself, when there is no answer it can use:

  • TIMEOUT: the API didn't answer within timeout.
  • NETWORK_ERROR: the API couldn't be reached, or the answer broke off.
  • INVALID_RESPONSE: the status said success, but the body was not an answer from the API: it had no data, had an error, or was not JSON. Most often, baseUrl is missing /v1.
  • HTTP_ERROR: an error came back that isn't in our format, most often from something between you and the API.

If you cancel a call with your own signal, the SDK throws the signal's reason instead. Options that can't work, like a baseUrl that isn't http or https, throw as soon as you create the client.

Check a webhook

verifyWebhook tells you whether a request really came from us. Give it your endpoint's secret, the raw body and the x-gmaps-signature header:

import { verifyWebhook } from "gmaps-sdk";

const body = await request.text();
const signed = await verifyWebhook(
  process.env.GMAPS_WEBHOOK_SECRET,
  body,
  request.headers.get("x-gmaps-signature"),
);
if (!signed) return new Response(null, { status: 400 });
const event = JSON.parse(body);

It returns false for every kind of failure: no header, a different secret, a changed body, or a signature more than 5 minutes old. Read the body as text and parse it only after the check: parsing it and writing it back changes its bytes. Webhooks covers the rest.

Types

Every answer is typed, and each answer type is exported by name for your own code:

import type { CollectionDto, PlaceDto, SearchResultDto } from "gmaps-sdk";

Each method's parameters are exported too, such as SearchPlacesParams, so you can type a call before you make it:

import type { SearchPlacesParams } from "gmaps-sdk";

const ask: SearchPlacesParams = {
  keywords: ["bakery"],
  geo: { location: "Curitiba, PR" }
};

Where it runs

  • Node 22 or newer, Bun, or any runtime with fetch, AbortSignal.any and AbortSignal.timeout.
  • It's an ES module and ships its own type declarations. Load it with import.
  • It has no dependencies. Calls use the runtime's own fetch, and verifyWebhook uses Web Crypto.
  • It runs in a browser too, but keep your key on a server. In a browser, anyone can read it.