---
title: "Everything about one publisher"
method: GET
path: "/v1/sites/{domain}"
tags: ["Catalogue"]
---

# Everything about one publisher

`GET /v1/sites/{domain}`

## Request

- Base URL: `https://api.placementapi.com`
- URL: `https://api.placementapi.com/v1/sites/{domain}`
- Auth: HTTP bearer

All live listings for one domain, cheapest first, with verification, graph, survival, link history, outcomes (3-buyer floor), placement score, demand, market reference per listing, supplier_count and metric history. The domain is canonicalised, so www/scheme/trailing-slash variants resolve to the same site.

## Path parameters

- `domain` string, required

## Response `200`

OK

- Site: The one place everything about a publisher is assembled. Use it for compare_domain and any 'tell me about this site' question rather than stitching offers together.
  - `configured` boolean
  - `found` true
  - `site` object
    - `site_id` string
    - `domain` string
    - `domain_hidden` boolean
    - `categories` string[]
    - `language` string, nullable
    - `country` string, nullable
    - `subdomain_of` string, nullable
  - `verification` object
    - `flag` '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.
    - `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.
  - `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).
  - `graph` object: Common Crawl web graph, root sites only (null fields on subdomains).
    - `outbound_external` integer, nullable
    - `inbound_domains` integer, nullable
    - `cc_pagerank` number, nullable
    - `cc_harmonic` 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
  - `link_history` object, nullable: The decay curve behind survival: our placements checked at each checkpoint and how many were live then. Same three-order floor.
    - `orders` integer
    - `by_checkpoint` object[]
      - `checkpoint` string
      - `checked` integer
      - `live` integer
    - `first_check_at` string
    - `last_check_at` string
  - `outcomes` object, nullable: Observed after placements on this publisher across every buyer; null under three distinct buyers. Observed, never caused.
  - `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
  - `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
  - `supplier_count` integer: Distinct suppliers carrying the domain — why a price spread exists — without saying who.
  - `spread_usd_minor` integer, nullable
  - `listings` SiteListing[]: Cheapest first.
    - `offer_id` string
    - `supplier` string: A per-response ordinal (A, B, C…) so you can see that listings come from different suppliers, without identifying them.
    - `placement_type` 'guest_post' | 'niche_edit' | 'link_insert' | 'homepage_link' | 'media_placement'
    - `price` object
      - `credits` integer, nullable
      - `usd_minor` integer, nullable
      - `currency` 'USD'
    - `comparable` boolean: False when the listing has no USD-normalised price.
    - `market_reference` MarketReference, nullable: The median ASK of comparable placements (same type, inventory mode and claimed-DR decade), not a clearing price — say 'market reference', never 'fair value'. own_asks is the spread of asks across suppliers for this exact domain and type, as counts and credits only. ask_history is null under 14 days of observations. Null on a thin band.
      - `reference_credits` integer
      - `method` 'band_median_v1'
      - `comparables_n` integer
      - `vs_reference_pct` number: This price against the reference, in percent; negative is cheaper.
      - `assessment` 'under' | 'at' | 'over': ±15% band around the reference.
      - `own_asks` object
        - `listings` integer
        - `suppliers` integer
        - `lowest` integer
        - `median` integer
        - `highest` integer
      - `ask_history` object, nullable
        - `first_seen_at` string, date-time
        - `days_observed` integer
        - `first_credits` integer
        - `change_pct` number
    - `observed_at` string
    - `age_hours` number
    - `stale` boolean
    - `stale_after_hours` integer
  - `metrics` object[]: Vendor claims and independent readings side by side, never merged.
    - `source` string
    - `rank` number, nullable
    - `traffic` number, nullable
    - `referring_domains` integer, nullable
    - `spam_score` number, nullable
    - `as_of` string, nullable
    - `fetched_at` string

## 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" }`.
- `404`: Domain not carried.

---

[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)
