---
title: "Search inventory"
method: GET
path: "/v1/offers"
tags: ["Catalogue"]
---

# Search inventory

`GET /v1/offers`

## Request

- Base URL: `https://api.placementapi.com`
- URL: `https://api.placementapi.com/v1/offers`
- Auth: HTTP bearer

Filtered catalogue rows, cheapest first, one per offer (a domain carried by several suppliers appears several times; POST /v1/quote collapses that). Unknown parameters are refused with 400 unknown_parameter. Rows count against the hourly and monthly read allowances; there is no geo filter — filter on the returned `geo` client-side.

## Query parameters

- `placement_type` 'guest_post' | 'niche_edit' | 'link_insert' | 'homepage_link' | 'media_placement'
- `inventory_mode` 'named' | 'pre_approval' | 'blind': The most important field. 'named' = a specific, identified domain you can inspect before ordering. 'pre_approval' = the vendor proposes candidate domains and the buyer approves them BEFORE placement. 'blind' = a promise (e.g. DR50+, finance) where the domain is assigned at fulfilment with no veto. Only 'named' carries a site_id. Never present a pre_approval or blind offer as though a specific site were secured, and never price-compare a specification against a named domain as the same product.
- `dr_min` number
- `rd_min` number
- `traffic_min` number
- `price_max` integer
- `price_min` integer
- `verified_only` 'true' | 'false' | '1' | '0'
- `include_restricted` 'true' | 'false' | '1' | '0'
- `plausibility_min_pct` number
- `supplier` string
- `niche` string
- `topic` string[]
- `accepts` string[]
- `limit` integer
- `offset` integer

## Response `200`

OK

- object
  - `configured` boolean
  - `count` integer
  - `limit` integer
  - `offset` integer
  - `offers` Offer[]
    - `offer_id` string: Public offer id. Its prefix is the opaque supplier token accepted by the `supplier` filter.
    - `placement_type` 'guest_post' | 'niche_edit' | 'link_insert' | 'homepage_link' | 'media_placement'
    - `inventory_mode` 'named' | 'pre_approval' | 'blind': The most important field. 'named' = a specific, identified domain you can inspect before ordering. 'pre_approval' = the vendor proposes candidate domains and the buyer approves them BEFORE placement. 'blind' = a promise (e.g. DR50+, finance) where the domain is assigned at fulfilment with no veto. Only 'named' carries a site_id. Never present a pre_approval or blind offer as though a specific site were secured, and never price-compare a specification against a named domain as the same product.
    - `site_id` string, nullable: Canonical domain. Only 'named' inventory carries one.
    - `price` object: The BUYER price: the figure debited at order time, so a basket costed from this endpoint will not move at checkout.
      - `credits` integer: Credits. 1 credit = 1 US cent.
      - `usd_minor` integer: The same number in cents, so you never have to convert.
      - `currency` 'USD'
      - `as_of` string, date-time
    - `plausibility` Plausibility, nullable: Where this price sits among comparable domains (same placement type, inventory mode and claimed-DR decade). flag 'low' = under the 10th percentile, which is bought authority more often than it is a bargain; 'high' = above the 90th. Null when the band had too few domains to describe.
      - `pct` number
      - `band` string
      - `flag` 'low' | 'ok' | 'high'
      - `n` integer: Domains behind the distribution.
    - `demand` Demand, nullable: Observed demand: distinct buyers who bought the domain in twelve months and the niches they bought for. Null under the three-buyer floor. Nothing about any one buyer is ever here.
      - `buyers_12m` integer
      - `purchases_12m` integer
      - `niches` object[]
        - `topic` string
        - `buyers` integer
    - `bulk_breaks` unknown
    - `link_attrs` unknown
    - `content` unknown
    - `guarantees` unknown
    - `metrics` OfferMetrics, nullable: What we know about the domain, and who says so. Claimed and measured figures are never merged: where they disagree, the measured one is the one to trust. Null when nothing is known.
      - `dr` number, nullable: Supplier-claimed Ahrefs-style score, 0-100; the scale dr_min filters on. On a subdomain this is the PARENT's number.
      - `traffic` number, nullable: Verified traffic where measured, else the claim.
      - `source` string: Provenance label for dr/traffic. Measured figures are in the verified_* fields.
      - `verification` 'ok' | 'suspect' | 'unverified' | 'null', nullable: Our independent verdict on the claimed authority; no supplier publishes it. 'suspect' = high link authority with little or no organic search presence, which is what a link farm looks like — do not plan around suspect domains. 'ok' = measured, and search presence matches the rating. 'unverified' = not measured yet, which is not a pass.
      - `quality_reasons` string[]: Which rules made verification read suspect: high_authority_no_traffic, high_authority_no_keywords, spam_score, subdomain_without_own_footprint, saturated_outbound. Empty when not suspect.
      - `subdomain_of` string, nullable: Parent domain when the host is a subdomain (blog.example.com, anything.blogspot.com). A subdomain is listed at its parent's DR and inherits none of it: read verified_dr, verified_referring_domains and verified_traffic, which are this host's own.
      - `restricted` string[]: gambling / adult / piracy when the site is one, from the domain name and a short topic list. Such rows appear only when asked for (include_restricted, or a topic/accepts naming the vertical).
      - `outbound_external` integer, nullable: Outside domains linked to (Common Crawl; root sites only).
      - `inbound_domains` integer, nullable
      - `cc_pagerank` number, nullable
      - `leader_links` LeaderLinks, nullable: Common Crawl web graph, root sites only (null on subdomains): how many of a topic's top-ranking sites the domain links to, keyed by topic.
      - `survival` Survival, nullable: What happened to the placements WE bought on this publisher. Our crawler re-checks every delivered link at 0/7/30/60/90/180/365 days. Null under three orders.
        - `orders` integer
        - `checks` integer
        - `live_share` number
        - `live_90d_share` number, nullable
        - `median_days_to_missing` number, nullable
        - `dofollow_share` number, nullable
        - `noindex_share` number, nullable
        - `updated_at` string, date-time
      - `placement_score` PlacementScore, nullable: Placement Score v1: { score 0-100, version, confidence high|medium|low, components each 0-100 or null, capped }. Weights (authority 25 / visibility 25 / quality 30 / graph 10 / survival 10) are stated, not fitted; unmeasured components drop out and the rest re-weight; a suspect publisher never scores above 40. Derived from the fields beside it, so it can be checked. Does NOT include observed effectiveness, relevance to your brief, or supplier reliability. Quote the components, not just the number.
        - `score` integer
        - `version` string
        - `confidence` 'high' | 'medium' | 'low': Share of the weight that had a measurement behind it.
        - `components` object
          - `authority` number, nullable
          - `visibility` number, nullable
          - `quality` number, nullable
          - `graph` number, nullable
          - `survival` number, nullable
        - `capped` 'suspect' | 'null', nullable
      - `verified_organic_keywords` integer, nullable: OUR independent measurement.
      - `verified_spam_score` number, nullable
      - `as_of` string, date-time, nullable
      - `verified_traffic` number, nullable: Independently measured monthly traffic.
      - `verified_dr` number, nullable: DataForSEO backlinks rank, 0-1000. Deliberately NOT rescaled to look like a DR.
      - `verified_referring_domains` integer, nullable
    - `turnaround_days_est` integer, nullable
    - `incentive` object, nullable: A discount for ordering before the next batch closes. Rewards WHEN you order, never how much; spend caps still apply.
      - `discount_pct` number
      - `price_with_incentive_credits` integer
      - `expires_at` string, date-time
      - `reason` string
    - `order_requirements` OrderRequirements, nullable: What you MUST send, what you MAY send, and what you can buy, normalised across suppliers. `required` lists the fields to include in POST /v1/orders. Read `confidence`: 'observed' and 'documented' are reliable; 'assumed' means the confirmation step may ask for something not listed.
      - `required` string[]: Always includes target_url; anchor_text is absent only where the publisher writes the anchor.
      - `optional` string[]
      - `content` object
        - `supplied_allowed` boolean: Whether you may write the article yourself.
        - `spec` ContentSpec, nullable: The brief to write against when the buyer supplies copy. Null when copy cannot be supplied.
          - `word_count_min` integer, nullable
          - `word_count_max` integer, nullable
          - `links_allowed` integer
          - `anchor_types_forbidden` string[]
          - `rules` string[]: Editorial rules in the publisher's own words.
        - `options` ContentOption[]
          - `tier` 'supplied' | 'included' | 'standard' | 'native' | 'papi_written'
          - `vendor_label` string, nullable: Pass as content_tier for content_mode 'service'.
          - `price_credits` integer: Add-on cost. Credits. 1 credit = 1 US cent.
          - `word_count` integer, nullable
          - `note` string, nullable
        - `due_days` integer, nullable: Days a supplied article may take to arrive after ordering (14); null where the publisher writes.
      - `speed` object
        - `standard_days` integer, nullable
        - `expedited_days` integer, nullable
        - `expedited_surcharge_bps` integer, nullable
      - `confirmation_required` boolean: True when a human at the supplier confirms the line before it is placed; treat the order as pending rather than done.
      - `rules` string[]: Placement rules the buyer must respect regardless of who writes the copy.
      - `confidence` 'observed' | 'documented' | 'assumed'
    - `niches_allowed` string[]: The supplier's own raw labels. Prefer topics.
    - `topics` string[]: Harmonised subjects.
    - `accepts` string[]: Empty means UNKNOWN, not 'accepts nothing'.
    - `geo` unknown
    - `languages` unknown
    - `publisher_notes` string, nullable
    - `observed_at` string, date-time
    - `age_hours` number
    - `stale` boolean
    - `stale_after_hours` integer

## Other responses

- `400`: Malformed request. `error` is `bad_request` (with a zod `detail`), `invalid_json`, or `unknown_parameter` / `unknown_field` (with `unknown[]` and `accepted[]`).
- `401`: Missing, malformed or revoked Bearer credential: `{ error: "unauthorized" }`.
- `429`: Rate limited. 1,000 requests and 20,000 returned rows per key per hour, a monthly row entitlement that grows with credits purchased, and a maximum offset of 10,000. Carries `Retry-After` and `X-RateLimit-*` / `X-Quota-Rows-*` headers.
- `503`: `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.

---

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