geogridapi 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,560 @@
1
+ declare const DEFAULT_BASE_URL = "https://api.geogridapi.io";
2
+ interface ClientOptions {
3
+ /** API key (`gga_live_…` or `gga_test_…`). Defaults to `process.env.GEOGRIDAPI_API_KEY`. */
4
+ apiKey?: string;
5
+ /** Defaults to `https://api.geogridapi.io`. */
6
+ baseURL?: string;
7
+ /** Timeout of each HTTP request, in milliseconds (default 60 000). */
8
+ timeoutMs?: number;
9
+ /** Retries on network errors, 429 and 5xx (default 2). Grid and batch creations are retried with the same idempotency key. */
10
+ maxRetries?: number;
11
+ /** Custom fetch implementation. Defaults to the global `fetch`. */
12
+ fetch?: typeof fetch;
13
+ }
14
+ interface RequestOptions {
15
+ signal?: AbortSignal;
16
+ timeoutMs?: number;
17
+ maxRetries?: number;
18
+ }
19
+ interface IdempotentRequestOptions extends RequestOptions {
20
+ /** Same key = same grid or batch, never charged twice. Generated when omitted. */
21
+ idempotencyKey?: string;
22
+ }
23
+ type QueryValue = string | number | boolean | undefined;
24
+ interface RequestSpec {
25
+ method: "GET" | "POST" | "PATCH" | "DELETE";
26
+ path: string;
27
+ query?: Record<string, QueryValue>;
28
+ body?: unknown;
29
+ idempotencyKey?: string;
30
+ /** Safe to send again after a network error or a 5xx. */
31
+ retryable: boolean;
32
+ binary?: boolean;
33
+ }
34
+ declare class Transport {
35
+ #private;
36
+ private readonly baseURL;
37
+ private readonly timeoutMs;
38
+ private readonly maxRetries;
39
+ private readonly fetchImpl;
40
+ constructor(options?: ClientOptions);
41
+ /** Whether the key is a test key (free mock grids). */
42
+ get isTestKey(): boolean;
43
+ private buildURL;
44
+ private headers;
45
+ request<T>(spec: RequestSpec, options?: RequestOptions): Promise<T>;
46
+ /** One HTTP exchange. A caller abort is rethrown as is; everything else becomes a GeoGridAPIError. */
47
+ private attempt;
48
+ }
49
+
50
+ type GridShape = "square" | "circle" | "diamond" | "hexagon" | "cross" | "ellipse" | "rectangle";
51
+ type GridDensity = "low" | "medium" | "high";
52
+ type GridSize = 3 | 5 | 7 | 9;
53
+ type GridStatus = "pending" | "running" | "completed" | "failed";
54
+ type KeyMode = "live" | "test";
55
+ interface Center {
56
+ lat: number;
57
+ lng: number;
58
+ }
59
+ /** Format shared by a grid, an estimate and a batch. */
60
+ interface GridFormat {
61
+ /** Grid shape (default `square`). */
62
+ shape?: GridShape;
63
+ /** Square only (default 7): side of the grid, size × size points. */
64
+ size?: GridSize;
65
+ /** Other shapes only (default `medium`). */
66
+ density?: GridDensity;
67
+ /** Distance from center to edge, 0.5 to 30 km (other shapes: from 1 km). */
68
+ radius_km?: number;
69
+ /** Search language, 2-letter code. Defaults to the account language. */
70
+ language?: string;
71
+ /** Search country, 2-letter ISO code. */
72
+ country?: string;
73
+ /** Public HTTPS URL called with the grid when it ends (`grid.completed` or `grid.failed`). */
74
+ webhook_url?: string;
75
+ }
76
+ interface TargetBase {
77
+ /** Keyword searched on Google Maps. */
78
+ keyword: string;
79
+ /** Grid center; defaults to the listing's location. */
80
+ center?: Center;
81
+ }
82
+ /** The listing to measure: a Google place id, or a name and city (first match). */
83
+ type GridTarget = TargetBase & ({
84
+ place_id: string;
85
+ query?: never;
86
+ } | {
87
+ query: string;
88
+ place_id?: never;
89
+ });
90
+ type CreateGridParams = GridTarget & GridFormat;
91
+ interface Place {
92
+ place_id: string;
93
+ name: string | null;
94
+ address: string | null;
95
+ lat: number;
96
+ lng: number;
97
+ }
98
+ /** A listing named without its coordinates (history, batch items). */
99
+ type PlaceRef = Pick<Place, "place_id" | "name" | "address">;
100
+ /** Why a grid or a batch item failed. */
101
+ interface GridError {
102
+ code: string;
103
+ message: string;
104
+ }
105
+ /** A search result; `lat`/`lng` are `null` when Google gives no public location (service-area business). */
106
+ interface FoundPlace extends PlaceRef {
107
+ lat: number | null;
108
+ lng: number | null;
109
+ }
110
+ interface PlaceList {
111
+ object: "list";
112
+ data: FoundPlace[];
113
+ }
114
+ interface GridSummary {
115
+ /** Mean rank over the points where the listing appears. */
116
+ average_rank: number | null;
117
+ /** Mean rank with misses counted as 21. */
118
+ average_rank_with_misses: number | null;
119
+ top3_points: number;
120
+ found_points: number;
121
+ }
122
+ interface TopListing {
123
+ rank: number;
124
+ place_id: string;
125
+ name: string;
126
+ }
127
+ interface GridPoint {
128
+ /** 0 = north. */
129
+ row: number;
130
+ /** 0 = west. */
131
+ col: number;
132
+ lat: number;
133
+ lng: number;
134
+ /** Position in the Google Maps results at this point; `null` = not in the top 20. */
135
+ rank: number | null;
136
+ /** Top 3 listings at this point. */
137
+ top: TopListing[];
138
+ }
139
+ interface Competitor {
140
+ place_id: string;
141
+ name: string;
142
+ /** Points where it is in the top 3. */
143
+ top3_points: number;
144
+ /** Points where it ranks first. */
145
+ first_points: number;
146
+ /** Points where it ranks ahead of your listing. */
147
+ ahead_points: number;
148
+ }
149
+ /** Change since the previous grid of the same map (same listing, keyword, format, radius and center). */
150
+ interface PreviousGrid {
151
+ grid_id: string;
152
+ created_at: string;
153
+ average_rank: number | null;
154
+ top3_points: number;
155
+ found_points: number;
156
+ changes: {
157
+ /** Negative = better. */
158
+ average_rank: number | null;
159
+ top3_points: number;
160
+ found_points: number;
161
+ };
162
+ ranks: (number | null)[][];
163
+ /** Places gained per cell (positive = better, a miss counts as 21). */
164
+ rank_changes: (number | null)[][];
165
+ points: {
166
+ improved: number;
167
+ worsened: number;
168
+ unchanged: number;
169
+ };
170
+ }
171
+ interface Grid {
172
+ id: string;
173
+ object: "grid";
174
+ status: GridStatus;
175
+ mode: KeyMode;
176
+ source: "api" | "mcp" | "console";
177
+ api_key_id: string | null;
178
+ place: Place;
179
+ keyword: string;
180
+ shape: GridShape;
181
+ density: GridDensity | null;
182
+ size: number;
183
+ rows: number;
184
+ cols: number;
185
+ radius_km: number;
186
+ language: string;
187
+ country: string | null;
188
+ /** Grid points measured (points at sea are neither measured nor charged). */
189
+ points_total: number;
190
+ /** Credits charged (0 with a test key). */
191
+ points_charged: number;
192
+ summary: GridSummary | null;
193
+ /** rows × cols matrix, row 0 = north, column 0 = west. `null` = not in the top 20 (or outside the shape). */
194
+ ranks: (number | null)[][] | null;
195
+ points?: GridPoint[] | null;
196
+ /** Who ranks ahead: the top 3 listings summed over the map. */
197
+ competitors?: Competitor[] | null;
198
+ previous?: PreviousGrid | null;
199
+ /** Why the grid failed (`null` unless `status` is `failed`). */
200
+ error: GridError | null;
201
+ /** Delivery of the `webhook_url`, when one was given. */
202
+ webhook?: {
203
+ status: string | null;
204
+ attempts: number;
205
+ } | null;
206
+ created_at: string;
207
+ completed_at: string | null;
208
+ }
209
+ interface ListGridsParams {
210
+ /** 1 to 100 (default 20). */
211
+ limit?: number;
212
+ /** `next_before` of the previous page. */
213
+ before?: string;
214
+ place_id?: string;
215
+ /** Exact keyword, case-insensitive. */
216
+ keyword?: string;
217
+ }
218
+ interface GridList {
219
+ object: "list";
220
+ data: Grid[];
221
+ has_more: boolean;
222
+ /** Pass as `before` to read the next page. */
223
+ next_before: string | null;
224
+ }
225
+ interface Estimate {
226
+ object: "estimate";
227
+ shape: GridShape;
228
+ size: number;
229
+ density: GridDensity | null;
230
+ rows: number;
231
+ cols: number;
232
+ radius_km: number;
233
+ /** Points that will be measured (points at sea removed). */
234
+ points: number;
235
+ points_at_sea: number;
236
+ /** `false`: the listing could not be located, `points` is an upper bound. */
237
+ exact: boolean;
238
+ /** Credits the grid will cost. */
239
+ credits: number;
240
+ balance?: number;
241
+ balance_after?: number;
242
+ enough?: boolean;
243
+ place: Place | null;
244
+ }
245
+ type HistoryParams = {
246
+ limit?: number;
247
+ } & ({
248
+ grid_id: string;
249
+ place_id?: never;
250
+ keyword?: never;
251
+ } | {
252
+ place_id: string;
253
+ keyword: string;
254
+ grid_id?: never;
255
+ });
256
+ interface HistoryEntry {
257
+ grid_id: string;
258
+ created_at: string;
259
+ average_rank: number | null;
260
+ top3_points: number;
261
+ found_points: number;
262
+ points_total: number;
263
+ }
264
+ interface History {
265
+ object: "history";
266
+ reference_grid_id: string;
267
+ mode: KeyMode;
268
+ place: PlaceRef;
269
+ keyword: string;
270
+ shape: GridShape;
271
+ density: GridDensity | null;
272
+ size: number;
273
+ rows: number;
274
+ cols: number;
275
+ radius_km: number;
276
+ /** Completed grids of the same map, oldest first. */
277
+ data: HistoryEntry[];
278
+ has_more: boolean;
279
+ change: {
280
+ since: string;
281
+ /** Negative = better. */
282
+ average_rank: number | null;
283
+ top3_points: number;
284
+ found_points: number;
285
+ } | null;
286
+ }
287
+ type CreateBatchParams = GridFormat & {
288
+ /** 1 to 50 grids: one listing with several keywords, or several listings with one keyword. */
289
+ items: GridTarget[];
290
+ };
291
+ type BatchItemStatus = "queued" | "pending" | "running" | "completed" | "failed";
292
+ interface BatchItem {
293
+ index: number;
294
+ keyword: string;
295
+ query: string | null;
296
+ place: PlaceRef | null;
297
+ status: BatchItemStatus;
298
+ grid_id: string | null;
299
+ points_total: number | null;
300
+ points_charged: number;
301
+ summary: Pick<GridSummary, "average_rank" | "top3_points" | "found_points"> | null;
302
+ error: GridError | null;
303
+ }
304
+ interface Batch {
305
+ id: string;
306
+ object: "batch";
307
+ status: "running" | "completed";
308
+ mode: KeyMode;
309
+ source: "api" | "mcp" | "console";
310
+ shape: GridShape;
311
+ size: number;
312
+ density: GridDensity | null;
313
+ radius_km: number;
314
+ items_count: number;
315
+ counts: Record<BatchItemStatus, number>;
316
+ credits_estimated: number;
317
+ credits_charged: number;
318
+ items: BatchItem[];
319
+ created_at: string;
320
+ launched_at: string | null;
321
+ }
322
+ interface CreateScheduleParams {
323
+ /** An existing completed grid to repeat. */
324
+ grid_id: string;
325
+ frequency: "weekly" | "monthly";
326
+ /** Weekly: 1 = Monday … 7 = Sunday (default 1). */
327
+ weekday?: number;
328
+ /** Monthly: day of the month, 1 to 28 (default 1). */
329
+ month_day?: number;
330
+ }
331
+ interface Schedule {
332
+ id: string;
333
+ object: "schedule";
334
+ status: "active" | "paused" | "stopped";
335
+ stopped_reason: string | null;
336
+ mode: KeyMode;
337
+ frequency: "weekly" | "monthly";
338
+ weekday: number | null;
339
+ month_day: number | null;
340
+ /** The listing and the grid center. */
341
+ place: Pick<Place, "place_id" | "name" | "lat" | "lng">;
342
+ keyword: string;
343
+ shape: GridShape;
344
+ density: GridDensity | null;
345
+ size: number;
346
+ radius_km: number;
347
+ points_per_run: number;
348
+ credits_per_month: number;
349
+ next_run_at: string | null;
350
+ last_run_at: string | null;
351
+ last_grid_id: string | null;
352
+ source_grid_id: string | null;
353
+ runs_count: number;
354
+ created_at: string;
355
+ }
356
+ interface ScheduleList {
357
+ object: "list";
358
+ data: Schedule[];
359
+ }
360
+ interface Balance {
361
+ object: "balance";
362
+ /** Credits left (1 credit = 1 grid point). */
363
+ points: number;
364
+ mode: KeyMode;
365
+ }
366
+ type WebhookEventType = "grid.completed" | "grid.failed";
367
+ interface WebhookEvent {
368
+ id: string;
369
+ object: "event";
370
+ type: WebhookEventType;
371
+ created_at: string;
372
+ data: Grid;
373
+ }
374
+
375
+ interface WaitOptions {
376
+ /** First delay between two status reads, in milliseconds (grids: 3 000, batches: 5 000). It then grows up to 10 s. */
377
+ pollIntervalMs?: number;
378
+ /** Give up after this many milliseconds (grids: 300 000, batches: 900 000). The work keeps running server side. */
379
+ timeoutMs?: number;
380
+ signal?: AbortSignal;
381
+ }
382
+ type RunOptions = IdempotentRequestOptions & {
383
+ wait?: WaitOptions;
384
+ };
385
+ declare class Places {
386
+ private readonly transport;
387
+ constructor(transport: Transport);
388
+ /** Finds a Google Business Profile by name and city, with its `place_id`. Free (a live key needs a positive balance). */
389
+ search(params: string | {
390
+ q: string;
391
+ language?: string;
392
+ }, options?: RequestOptions): Promise<PlaceList>;
393
+ }
394
+ declare class Grids {
395
+ private readonly transport;
396
+ constructor(transport: Transport);
397
+ /**
398
+ * Starts a grid (status `pending`); it completes in 20 seconds to 2 minutes.
399
+ * Costs 1 credit per point, refunded if the grid fails. Use `run` to wait for the result.
400
+ * Retries inside this call reuse one idempotency key; pass your own `idempotencyKey` to make your own retries safe too.
401
+ */
402
+ create(params: CreateGridParams, options?: IdempotentRequestOptions): Promise<Grid>;
403
+ /** Exact number of points and credits a grid would cost. Free, nothing is reserved. */
404
+ estimate(params: CreateGridParams, options?: RequestOptions): Promise<Estimate>;
405
+ /** Status and result of a grid: ranks, summary, competitors ahead and change since the previous grid. */
406
+ get(id: string, options?: RequestOptions): Promise<Grid>;
407
+ /** One page of grids, newest first, without point details. */
408
+ list(params?: ListGridsParams, options?: RequestOptions): Promise<GridList>;
409
+ /** Every grid, newest first, reading the pages as you iterate. */
410
+ listAll(params?: Omit<ListGridsParams, "before">, options?: RequestOptions): AsyncGenerator<Grid>;
411
+ /** PNG heatmap (1200 × 1092) of a completed grid. Free. */
412
+ image(id: string, params?: {
413
+ lang?: "en" | "fr";
414
+ }, options?: RequestOptions): Promise<Uint8Array>;
415
+ /** Reads the grid until it is `completed` or `failed`. */
416
+ wait(id: string, options?: WaitOptions): Promise<Grid>;
417
+ /** Starts a grid and waits for its result (`completed` or `failed`; a failed grid is refunded). */
418
+ run(params: CreateGridParams, options?: RunOptions): Promise<Grid>;
419
+ }
420
+ declare class Batches {
421
+ private readonly transport;
422
+ constructor(transport: Transport);
423
+ /**
424
+ * Up to 50 grids with the same format: one listing with several keywords, or several listings with one keyword.
425
+ * Each grid is charged when it starts and refunded if it fails.
426
+ */
427
+ create(params: CreateBatchParams, options?: IdempotentRequestOptions): Promise<Batch>;
428
+ /** A batch and the status of each of its grids. */
429
+ get(id: string, options?: RequestOptions): Promise<Batch>;
430
+ /** Reads the batch until every grid is finished. */
431
+ wait(id: string, options?: WaitOptions): Promise<Batch>;
432
+ /** Starts a batch and waits until every grid is finished. */
433
+ run(params: CreateBatchParams, options?: RunOptions): Promise<Batch>;
434
+ }
435
+ declare class HistoryResource {
436
+ private readonly transport;
437
+ constructor(transport: Transport);
438
+ /** Every completed grid of the same map, oldest first, and the change between the first and the last. Free. */
439
+ get(params: HistoryParams, options?: RequestOptions): Promise<History>;
440
+ }
441
+ declare class Schedules {
442
+ private readonly transport;
443
+ constructor(transport: Transport);
444
+ /** Repeats a completed grid every week or every month. Each run costs the grid's credits. */
445
+ create(params: CreateScheduleParams, options?: RequestOptions): Promise<Schedule>;
446
+ list(options?: RequestOptions): Promise<ScheduleList>;
447
+ /** Pauses (`paused`) or resumes (`active`) a schedule. */
448
+ update(id: string, params: {
449
+ status: "active" | "paused";
450
+ }, options?: RequestOptions): Promise<Schedule>;
451
+ /** Stops repeating a grid for good. Grids already run are kept. Not retried: a lost response would turn into a 404. */
452
+ delete(id: string, options?: RequestOptions): Promise<Record<string, unknown>>;
453
+ }
454
+ declare class BalanceResource {
455
+ private readonly transport;
456
+ constructor(transport: Transport);
457
+ /** Credits left on the account (1 credit = 1 grid point). */
458
+ get(options?: RequestOptions): Promise<Balance>;
459
+ }
460
+
461
+ interface GeoGridAPIErrorOptions {
462
+ status?: number | null;
463
+ code?: string;
464
+ retryAfter?: number | null;
465
+ body?: unknown;
466
+ cause?: unknown;
467
+ gridId?: string;
468
+ batchId?: string;
469
+ }
470
+ declare class GeoGridAPIError extends Error {
471
+ /** HTTP status, `null` when no response was received. */
472
+ readonly status: number | null;
473
+ /** API error code (`invalid_body`, `insufficient_points`, `rate_limited`…). */
474
+ readonly code: string;
475
+ /** Seconds to wait before retrying, from the `Retry-After` header. */
476
+ readonly retryAfter: number | null;
477
+ /** Parsed error body, when there is one. */
478
+ readonly body: unknown;
479
+ /** Set when the error happened after a grid was created (while waiting for it): read it later with `grids.get`. */
480
+ gridId?: string;
481
+ /** Set when the error happened after a batch was created (while waiting for it): read it later with `batches.get`. */
482
+ batchId?: string;
483
+ constructor(message: string, options?: GeoGridAPIErrorOptions);
484
+ }
485
+ /** 400: invalid body or parameters. */
486
+ declare class InvalidRequestError extends GeoGridAPIError {
487
+ }
488
+ /** 401: missing, malformed, unknown or revoked API key. */
489
+ declare class AuthenticationError extends GeoGridAPIError {
490
+ }
491
+ /** 402: not enough credits for this grid or batch. */
492
+ declare class InsufficientCreditsError extends GeoGridAPIError {
493
+ }
494
+ /** 404: unknown grid, batch or schedule, or no listing matches. */
495
+ declare class NotFoundError extends GeoGridAPIError {
496
+ }
497
+ /** 409: conflicting state (grid not ready, already scheduled, idempotency key reused…). */
498
+ declare class ConflictError extends GeoGridAPIError {
499
+ }
500
+ /** 422: an account limit is reached (`schedule_limit`). */
501
+ declare class UnprocessableEntityError extends GeoGridAPIError {
502
+ }
503
+ /** 429: too many requests or too many grids still running. */
504
+ declare class RateLimitError extends GeoGridAPIError {
505
+ }
506
+ /** 5xx: the API or the ranking engine is temporarily unavailable. */
507
+ declare class APIError extends GeoGridAPIError {
508
+ }
509
+ /** No response: network failure or request timeout. */
510
+ declare class APIConnectionError extends GeoGridAPIError {
511
+ }
512
+ /** A grid or batch did not finish within the wait timeout. It keeps running server side. */
513
+ declare class WaitTimeoutError extends GeoGridAPIError {
514
+ }
515
+
516
+ declare const SIGNATURE_HEADER = "GeoGridAPI-Signature";
517
+ declare const DEFAULT_TOLERANCE_SECONDS = 300;
518
+ interface VerifyOptions {
519
+ /** Maximum age of the signature, in seconds (default 300). */
520
+ toleranceSeconds?: number;
521
+ /** Current time in unix seconds, for tests. */
522
+ now?: number;
523
+ }
524
+ /** Thrown by `constructEvent` when the signature is missing, invalid or too old. */
525
+ declare class WebhookSignatureError extends GeoGridAPIError {
526
+ }
527
+ /**
528
+ * Checks the `GeoGridAPI-Signature` header of a webhook call.
529
+ * `payload` must be the raw request body, exactly as received (not re-serialized JSON).
530
+ */
531
+ declare function verifyWebhookSignature(payload: string | Uint8Array, header: string | null | undefined, secret: string, options?: VerifyOptions): Promise<boolean>;
532
+ /** Verifies the signature, then parses the event. Throws `WebhookSignatureError` when it does not match. */
533
+ declare function constructEvent(payload: string | Uint8Array, header: string | null | undefined, secret: string, options?: VerifyOptions): Promise<WebhookEvent>;
534
+
535
+ declare const VERSION = "0.1.0";
536
+
537
+ /**
538
+ * GeoGridAPI client.
539
+ *
540
+ * ```ts
541
+ * import { GeoGridAPI } from "geogridapi";
542
+ * const gga = new GeoGridAPI(); // reads GEOGRIDAPI_API_KEY
543
+ * const grid = await gga.grids.run({ query: "Joe's Pizza, New York", keyword: "pizza", size: 7 });
544
+ * console.log(grid.summary?.average_rank, grid.ranks);
545
+ * ```
546
+ */
547
+ declare class GeoGridAPI {
548
+ readonly places: Places;
549
+ readonly grids: Grids;
550
+ readonly batches: Batches;
551
+ readonly history: HistoryResource;
552
+ readonly schedules: Schedules;
553
+ readonly balance: BalanceResource;
554
+ private readonly transport;
555
+ constructor(options?: ClientOptions);
556
+ /** Whether the client uses a test key (`gga_test_`: free mock grids). */
557
+ get isTestKey(): boolean;
558
+ }
559
+
560
+ export { APIConnectionError, APIError, AuthenticationError, type Balance, type Batch, type BatchItem, type BatchItemStatus, type Center, type ClientOptions, type Competitor, ConflictError, type CreateBatchParams, type CreateGridParams, type CreateScheduleParams, DEFAULT_BASE_URL, DEFAULT_TOLERANCE_SECONDS, type Estimate, type FoundPlace, GeoGridAPI, GeoGridAPIError, type GeoGridAPIErrorOptions, type Grid, type GridDensity, type GridError, type GridFormat, type GridList, type GridPoint, type GridShape, type GridSize, type GridStatus, type GridSummary, type GridTarget, type History, type HistoryEntry, type HistoryParams, type IdempotentRequestOptions, InsufficientCreditsError, InvalidRequestError, type KeyMode, type ListGridsParams, NotFoundError, type Place, type PlaceList, type PlaceRef, type PreviousGrid, RateLimitError, type RequestOptions, type RunOptions, SIGNATURE_HEADER, type Schedule, type ScheduleList, type TopListing, UnprocessableEntityError, VERSION, type VerifyOptions, type WaitOptions, WaitTimeoutError, type WebhookEvent, type WebhookEventType, WebhookSignatureError, constructEvent, GeoGridAPI as default, verifyWebhookSignature };