placementapi-com Search Console API

A customer's own retained Search Console rows. Read-only, never shared.

Operations 3

GET /v1/gsc/properties Your Search Console properties and selection #
PATCH /v1/gsc/properties Choose which properties to pull #
GET /v1/gsc/daily Your Search Console rows, by day #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/placementapi-com:placementapi-com-search-console-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Discovery needs no key. Ratings and market analysis are Pro.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

placementapi-com-search-console-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: Placement Search Console API
  version: 1.0.0
  description: 'Normalised backlink placement inventory, deduplicated across multiple suppliers, with independent verification, held quotes, ordering, content handling and post-placement outcome tracking. Every price is the buyer price in credits (1 credit = 1 US cent), already normalised from supplier currencies, so the figure you plan with is the figure you are charged. Supplier identity is never exposed.


    Authenticate with a `pk_live_…` API key as a Bearer token. Keys carry scopes: `read` (catalogue, quotes, account reads) and `order` (anything that moves money). An MCP server exposing the same operations as tools is available at https://api.placementapi.com/mcp with the same Bearer key; the tools call these endpoints, so pricing and verification are identical either way. Unknown query parameters and brief fields are refused with 400 rather than ignored.'
  contact:
    email: hello@placementapi.com
  termsOfService: https://placementapi.com/terms
servers:
- url: https://api.placementapi.com
security:
- apiKey: []
tags:
- name: Search Console
  description: A customer's own retained Search Console rows. Read-only, never shared.
paths:
  /v1/gsc/properties:
    get:
      tags:
      - Search Console
      operationId: gsc_properties
      summary: Your Search Console properties and selection
      description: 'The properties your Google connection can see, with your selection and each property''s pull state. Read-only unless `refresh=1`, which re-asks Google for the current property list. `pull_state` is additive: `queued`/`running` mean an immediate first pull is in progress, `done` means the requested window was processed under Google''s existing row caps (not a complete Google total), `failed` is a terminal permission/revocation outcome, and null means the property is unselected or has never been requested. A property pulled before the first-pull queue existed derives `done` from `pulled_through` when it has no queue state of its own. Read with a customer key (`read` scope) or a dashboard session; property names are private, so every response is `private, no-store` and private property URLs are never logged. Every row is scoped to your account.'
      parameters:
      - name: refresh
        in: query
        required: false
        schema:
          type: string
          enum:
          - '1'
        description: Re-ask Google for the current property list. Retires properties that vanished; keeps your selection.
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  properties:
                    type: array
                    items:
                      type: object
                      required:
                      - property
                      - permission_level
                      - selected
                      - pulled_through
                      - last_pull_at
                      - last_error
                      - pull_state
                      properties:
                        property:
                          type: string
                          description: 'Exactly as Google names it: `sc-domain:example.com` or `https://www.example.com/`.'
                        permission_level:
                          type:
                          - string
                          - 'null'
                          description: siteOwner | siteFullUser | siteRestrictedUser | siteUnverifiedUser, or null.
                        selected:
                          type: boolean
                          description: Whether the nightly pull keeps this property current.
                        pulled_through:
                          type:
                          - string
                          - 'null'
                          format: date
                          description: Last day actually pulled, or null.
                        last_pull_at:
                          type:
                          - string
                          - 'null'
                          format: date-time
                          description: Most recent pull attempt, which may have failed; null if never.
                        last_error:
                          type:
                          - string
                          - 'null'
                          description: Error from the most recent attempt, or null. Immediate first pulls use short error codes.
                        pull_state:
                          type:
                          - string
                          - 'null'
                          enum:
                          - queued
                          - running
                          - done
                          - failed
                          - null
                          description: 'Additive immediate-first-pull state. `queued`/`running`: an immediate import is pending or in progress. `done`: the requested window was processed under Google''s existing row caps — not a complete Google total. `failed`: terminal permission/revocation outcome. null: unselected or never requested. A property pulled before the queue existed derives `done` from `pulled_through` when it has no queue state of its own.'
                  selected:
                    type: array
                    items:
                      type: string
                    description: The selected property names, in the same order as `properties`.
                  next:
                    type: string
                    description: A short hint about what happens next; wording is not part of the contract.
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          description: '`insufficient_scope` when the key lacks the `read` scope.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: '`not_connected`: no Google connection for this account.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '503':
          description: '`server_misconfigured`, or `gsc_not_configured` when `refresh=1` and the Google client is not configured.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    patch:
      tags:
      - Search Console
      operationId: gsc_select_properties
      summary: Choose which properties to pull
      description: 'Set which properties the nightly pull keeps current. Selecting a property that has never been pulled queues an immediate first pull: the response returns at once with its additive `pull_state`, and a bounded background task imports the last 180 days (or a larger configured backfill) for your account only. The queue is durable — a five-minute recovery sweep finishes anything the background task does not, and the token-free selection write means repeat submissions never reset a queued, running, failed or completed job, its retry timer or its fixed window. A `failed` job is retried only after an explicit Google reconnect, never by resubmitting the same selection. A missing Google configuration does not discard the queue or report the import complete. Requires the customer key''s `read` scope; a revoked connection is refused with `reconnect_required` until it is reconnected, and the response is `private, no-store`. Unknown properties are refused; the daily read contract is unchanged.'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - selected
              additionalProperties: false
              properties:
                selected:
                  type: array
                  maxItems: 50
                  items:
                    type: string
                    minLength: 1
                    maxLength: 500
                  description: The exact property names to keep selected. Properties not listed are deselected; their stored rows are kept.
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  properties:
                    type: array
                    items:
                      type: object
                      required:
                      - property
                      - permission_level
                      - selected
                      - pulled_through
                      - last_pull_at
                      - last_error
                      - pull_state
                      properties:
                        property:
                          type: string
                          description: 'Exactly as Google names it: `sc-domain:example.com` or `https://www.example.com/`.'
                        permission_level:
                          type:
                          - string
                          - 'null'
                          description: siteOwner | siteFullUser | siteRestrictedUser | siteUnverifiedUser, or null.
                        selected:
                          type: boolean
                          description: Whether the nightly pull keeps this property current.
                        pulled_through:
                          type:
                          - string
                          - 'null'
                          format: date
                          description: Last day actually pulled, or null.
                        last_pull_at:
                          type:
                          - string
                          - 'null'
                          format: date-time
                          description: Most recent pull attempt, which may have failed; null if never.
                        last_error:
                          type:
                          - string
                          - 'null'
                          description: Error from the most recent attempt, or null. Immediate first pulls use short error codes.
                        pull_state:
                          type:
                          - string
                          - 'null'
                          enum:
                          - queued
                          - running
                          - done
                          - failed
                          - null
                          description: 'Additive immediate-first-pull state. `queued`/`running`: an immediate import is pending or in progress. `done`: the requested window was processed under Google''s existing row caps — not a complete Google total. `failed`: terminal permission/revocation outcome. null: unselected or never requested. A property pulled before the queue existed derives `done` from `pulled_through` when it has no queue state of its own.'
                  selected:
                    type: array
                    items:
                      type: string
                    description: The selected property names, in the same order as `properties`.
                  next:
                    type: string
                    description: A short hint about what happens next; wording is not part of the contract.
        '400':
          description: '`bad_request` for a malformed body; `unknown_property` when a name is not visible to this connection.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: '`unauthorized`, or `reconnect_required` when the stored connection has been revoked and must be reconnected before work can be queued.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: '`insufficient_scope` when the key lacks the `read` scope.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: '`not_connected`: no Google connection for this account.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '503':
          $ref: '#/components/responses/Unavailable'
  /v1/gsc/daily:
    get:
      tags:
      - Search Console
      operationId: gsc_daily_rows
      summary: Your Search Console rows, by day
      description: 'Your own retained Search Console rows for one selected property, aggregated by `date` × `page` (or `date` × `page` × `query` with `dimensions=page,query,date`). Rows always carry `date`, never `day`. Read-only and additive: no Google request, no pull, no write. Every row is filtered to your account and the exact property. Aggregation uses the real page × query grain only (`page` and `query` non-empty, all-device, all-country): `clicks` and `impressions` are summed, `ctr` is `sum(clicks)/sum(impressions)` (0 with no impressions) and `position` is the impression-weighted `sum(position*impressions)/sum(impressions)` (null with no impressions) — CTRs and positions are never averaged unweighted. An empty `rows` array means no retained rows for the window, never zero-filled days. Coverage is honest, not complete: Google returns only its top rows per request and the pull stops after four pages per chunk, so anonymized queries and capped rows are absent. `pulled_through` is the last day actually pulled; `last_pulled_at` is the most recent recorded pull attempt for the property, which may itself have failed, so neither claims the window is complete. These are observed search metrics; they do not show that any link or placement caused a change. Read with a customer key (`read` scope) or a dashboard session; the operator token is refused, and any Authorization header (even empty or whitespace) is answered by key authentication rather than a session. Errors and successes are private and no-store, and private URLs, queries and rows are never logged.'
      parameters:
      - name: property
        in: query
        required: true
        schema:
          type: string
          minLength: 1
          maxLength: 500
        description: The Search Console property to read, exactly as Google names it (e.g. `sc-domain:example.com`). It must be visible to your connection and selected for pulling, else 403 property_not_selected.
      - name: from
        in: query
        required: true
        schema:
          type: string
          format: date
        description: Inclusive start day, `YYYY-MM-DD`.
      - name: to
        in: query
        required: true
        schema:
          type: string
          format: date
        description: Inclusive end day, `YYYY-MM-DD`. `from` and `to` may be at most 180 days apart.
      - name: page
        in: query
        schema:
          type: array
          maxItems: 100
          items:
            type: string
            format: uri
            maxLength: 4096
        style: form
        explode: true
        description: Exact page URL to filter to. Repeat for several (at most 100); the exact spelling is preserved and duplicates are collapsed.
      - name: dimensions
        in: query
        required: false
        schema:
          type: string
          enum:
          - page,date
          - page,query,date
          default: page,date
        description: '`page,date` sums each page''s queries together; `page,query,date` keeps the query column.'
      - name: cursor
        in: query
        required: false
        schema:
          type: string
          maxLength: 1024
        description: Opaque continuation cursor from `next_cursor`. It carries only the last group's day and an internal decimal id, is bound by hash to the account and the full filter set, and is only a position marker (never an authorization credential, and never a copy of a page URL or query). A cursor for another account, a changed filter set or a date outside the requested window is refused with 400 bad_cursor. Re-authorisation and property selection are re-checked on every request.
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  property:
                    type: string
                  from:
                    type: string
                    format: date
                  to:
                    type: string
                    format: date
                  dimensions:
                    type: array
                    items:
                      type: string
                      enum:
                      - page
                      - query
                      - date
                  last_pulled_at:
                    type:
                    - string
                    - 'null'
                    format: date-time
                    description: RFC 3339 timestamp of the most recent recorded pull attempt for this property, or null if never. It may record a failed attempt, so it does not prove the window is complete; it is not reported as zero. The raw database timestamp text is never exposed.
                  pulled_through:
                    type:
                    - string
                    - 'null'
                    format: date
                    description: Last day actually pulled for this property, or null.
                  count:
                    type: integer
                    description: Rows in this response (not the total matching rows).
                  rows:
                    type: array
                    maxItems: 50000
                    description: At most 50,000 rows. The 3,000,000-byte response cap may produce a shorter page with a continuation cursor.
                    items:
                      type: object
                      required:
                      - date
                      - page
                      - clicks
                      - impressions
                      - ctr
                      - position
                      properties:
                        date:
                          type: string
                          format: date
                          description: Google's stored day (YYYY-MM-DD), with no timezone conversion.
                        page:
                          type: string
                          description: Exact page URL, as stored.
                        query:
                          type: string
                          description: Present only in the page,query,date dimension.
                        clicks:
                          type: integer
                        impressions:
                          type: integer
                        ctr:
                          type: number
                          description: sum(clicks)/sum(impressions); 0 when there were no impressions.
                        position:
                          type:
                          - number
                          - 'null'
                          description: Impression-weighted average position; null when there were no impressions.
                  next_cursor:
                    type:
                    - string
                    - 'null'
                    description: Pass back as `cursor` for the next page. Null when the window is exhausted. Returned when either the row or serialized-byte bound was reached; the serialized body (envelope and cursor included) stays under the byte cap, and boundaries never skip a row. Changing source data between requests is not a fixed snapshot.
                  note:
                    type: string
                    description: 'Coverage and interpretation note: what the rows are not (a complete Google total) and that they show no causality.'
        '400':
          description: '`bad_date`, `range_too_long`, `bad_cursor`, `bad_request` (including unknown dimensions or an invalid page URL), `duplicate_parameter`, or `unknown_parameter`.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          description: '`insufficient_scope` when the key lacks the `read` scope; `customer_key_required` for the operator token; `property_not_selected` when the property is missing, unselected or not visible to this connection.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: '`not_connected`: no Google connection for this account, or the connection was revoked.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: '`response_too_large` when a single aggregated row cannot fit the response byte cap (the row is never silently skipped).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '503':
          $ref: '#/components/responses/Unavailable'
components:
  schemas:
    Error:
      type: object
      required:
      - error
      properties:
        error:
          type: string
          description: Machine-readable code, snake_case.
        detail:
          description: Human-readable explanation, or a zod flatten() object on validation failures.
      additionalProperties: true
  responses:
    Unavailable:
      description: '`server_misconfigured` when the store is not configured; `upstream_unfunded` when the measurement provider''s prepaid balance is exhausted (fresh profiles, gap maps and SERPs pause; cached results and catalogue are unaffected); `upstream_unavailable` on a provider error; `payments_unavailable` when card payments are not configured.'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Unauthorized:
      description: 'Missing, malformed or revoked Bearer credential: `{ error: "unauthorized" }`.'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
  securitySchemes:
    apiKey:
      type: http
      scheme: bearer
      description: 'Customer API key (`pk_live_…`) in `Authorization: Bearer <key>`. Issue from POST /v1/keys or the dashboard. Endpoints marked session-only reject API keys.'