---
title: "Set geo targeting"
method: POST
path: "/api/v1/campaigns/{id}/geo-targeting"
tags: ["campaigns"]
---

# Set geo targeting

`POST /api/v1/campaigns/{id}/geo-targeting`

## Request

- Base URL: `https://api.growomat.com`
- URL: `https://api.growomat.com/api/v1/campaigns/{id}/geo-targeting`
- Auth: HTTP bearer

One call for the whole location set. Send either the structured `geoTargets` array, or friendly `include`, `exclude` and `radii` lists of place names that the server resolves for you, plus the campaign-level `locationIntent` (who counts as being in a place).

Rules:

- An empty `geoTargets` is valid and means "serve everywhere".
- A radius circle cannot be excluded; exclude a named area instead.
- A set made only of exclusions is rejected.

Pass `dry_run: true` to get the resolved rows and a plain-language sentence describing the effective targeting, without changing the campaign. Changes reach the ad platforms on the next sync.

**Errors**

- `404 NOT_FOUND`: no campaign with this id in the active Space.
- `400 RADIUS_EXCLUSION_UNSUPPORTED`: a radius row was marked `negative`.
- `400 ONLY_EXCLUSIONS`: every row is an exclusion.
- `400 VALIDATION_ERROR`: the body failed validation; `details.issues[]` names each field.

## Path parameters

- `id` string, required

## Headers

- `X-Space-Id` string

## Request body

- SetGeoTargeting: Human-friendly location input. Provide the structured `geoTargets` array, or `include`, `exclude` and `radii` lists of place names that the server resolves, or only `locationIntent` to change that alone. `dry_run: true` returns the resolved rows and a plain-language sentence describing the effective targeting, without changing the campaign.
  - `geoTargets` GeoTarget[]: The full structured list, replacing the current one.
    - `id` string: Stable row id. Generated when omitted.
    - `kind` 'AREA' | 'RADIUS', required: `AREA` is a named place, from a country down to a postcode. `RADIUS` is a circle around a point.
    - `negative` boolean: `true` excludes this row from the included set. Not allowed on `RADIUS` rows (`400 RADIUS_EXCLUSION_UNSUPPORTED`), and a set where every row is negative is rejected (`400 ONLY_EXCLUSIONS`).
    - `bidAdjustment` number: Percent bid adjustment for this row. Included rows only.
    - `reach` number: Audience estimate as reported by the platform, when it gave one. Read-only; never estimated by Growomat.
    - `criterionId` string, nullable: Areas: the Google Ads geo target id.
    - `msLocationId` string, nullable: Areas: the Microsoft Advertising location id.
    - `canonicalId` string, nullable: Areas: Growomat's platform-independent id for the place.
    - `name` string, required: Display name of the place or circle.
    - `type` 'COUNTRY' | 'REGION' | 'METRO' | 'CITY' | 'DISTRICT' | 'POSTAL_CODE' | 'AIRPORT' | 'UNIVERSITY' | 'GLOBAL': Areas only: what kind of place this is.
    - `countryCode` string, nullable: Areas: ISO 3166-1 country code.
    - `lat` number: Circles only: latitude of the centre.
    - `lng` number: Circles only: longitude of the centre.
    - `radius` number: Circles only. Clamped to the platform's range (1 to 500) when pushed; a clamp is reported as a `RADIUS_CLAMPED_TO_MIN` warning.
    - `unit` 'km' | 'mi': Circles only: the unit of `radius`.
  - `include` SetGeoTargetingArea[]: Places to show ads in.
    - union: A named place, as a plain string or as an object with hints for resolving it.
      - string: A place name, for example "Germany", "Berlin" or "Mitte".
      - object
        - `name` string, required: The place name.
        - `type` string: What kind of place to look for, for example `CITY` or `DISTRICT`.
        - `countryCode` string: ISO country code to search within.
  - `exclude` SetGeoTargetingArea[]: Places to carve out of the included set.
    - union: A named place, as a plain string or as an object with hints for resolving it.
      - string: A place name, for example "Germany", "Berlin" or "Mitte".
      - object
        - `name` string, required: The place name.
        - `type` string: What kind of place to look for, for example `CITY` or `DISTRICT`.
        - `countryCode` string: ISO country code to search within.
  - `radii` SetGeoTargetingRadius[]: Circles to show ads in.
    - `around` string: Place name to centre on; resolved to coordinates on the server. Alternative to `lat` and `lng`.
    - `lat` number: Latitude of the centre.
    - `lng` number: Longitude of the centre.
    - `radius` number, required: Radius of the circle.
    - `unit` 'km' | 'mi': Unit of `radius`.
    - `name` string: Display label for the circle.
    - `countryCode` string: ISO country code to resolve `around` within.
  - `locationIntent` 'PRESENCE' | 'PRESENCE_OR_INTEREST': Who counts as being in the targeted places. See `CampaignCreate.locationIntent`.
  - `dry_run` boolean: When `true`, preview the resolved set and the sentence without changing the campaign.

## Response `200`

OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.

- SuccessEnvelope: The shape of every successful response.
  - `data` unknown, required
  - `meta` object: Present on list responses.
    - `nextCursor` union: Pass as `cursor` to get the next page. `null` on the last page.
      - string
      - number
    - `total` integer: Number of items in this page.

## Other responses

- `201`: Created. The new resource is in `data`.
- `400`: The request is malformed or fails validation. `error.code` is `VALIDATION_ERROR` (with `details.issues[]` naming each field), `INVALID_REQUEST` (the body is not valid JSON or has the wrong shape), or one of the specific codes listed in this operation's description.
- `401`: The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).
- `403`: The token lacks the scope this operation needs (`INSUFFICIENT_SCOPE`), the plan is read-only (`READ_ONLY_MODE`), or the caller is not a member of the Space named in `X-Space-Id` (`NOT_A_MEMBER`).
- `404`: No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).
- `409`: The request conflicts with the current state, for example a delete blocked by dependents or a sync already running. This operation's description names the `error.code` values it can return.
- `429`: Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.

---

[API](https://skmtc.dev/growomat/apis/growomat-public-api.md) · [All operations](https://skmtc.dev/growomat/apis/growomat-public-api/llms.txt) · [OpenAPI document](https://skmtc.dev/growomat/apis/growomat-public-api/revisions/f02611b531aa?raw)
