# Target API — search or browse the target.com (US) catalogue and pull one product in full: the UPC barcode, shipping weight and package dimensions, the complete image gallery, the whole colour/size variant matrix with each variant's own price, stock and native Target id, the specification table, brand, availability, star rating with its full distribution, and customer reviews inline — one call, no API key, account or cookie required

> Find products on target.com (US). Give a free-text `query` ('coffee maker', 'lego star wars', 'nike running shoes') to run Target's own site search, or a `category` path ('/c/cpus/-/N-01vhh', '/b/apple/-/N-5y3ej') to browse a Target category or brand page. Every product comes back with BOTH of Target's ids — the parent product id and the specific colour/size id — plus the name, brand, category, image, current price, the crossed-out list price when it is on sale, the star rating and rating count, and the product URL. Narrow with `sort`, `min_price` and `max_price`; page with `page` and `max_results`.
> ReefAPI engine `target` · 2 endpoints · clean JSON, no scraping or browsers to manage.

## How to call
- **Endpoint:** `POST https://api.reefapi.com/target/v1/<action>` with a JSON body.
- **Auth:** header `x-api-key: <YOUR_REEFAPI_KEY>` — create one free (1,000 credits, no card): https://reefapi.com/signup
- **Response (every call):** `{ ok: boolean, data: ..., meta: { record_count, credits, ... }, error: { code, message } }` — branch on `ok`. Failed or blocked calls are free.
- **One key + one shared credit pool** across every ReefAPI API. Per-call credits are listed on each endpoint below.
- **Use it from an AI agent (MCP):** connect `https://api.reefapi.com/mcp` (remote streamable-http, `Authorization: Bearer <key>`) and your assistant can call these actions directly.

## Endpoints

### POST /target/v1/search — 1 credit
Find products on target.com (US). Give a free-text `query` ('coffee maker', 'lego star wars', 'nike running shoes') to run Target's own site search, or a `category` path ('/c/cpus/-/N-01vhh', '/b/apple/-/N-5y3ej') to browse a Target category or brand page. Every product comes back with BOTH of Target's ids — the parent product id and the specific colour/size id — plus the name, brand, category, image, current price, the crossed-out list price when it is on sale, the star rating and rating count, and the product URL. Narrow with `sort`, `min_price` and `max_price`; page with `page` and `max_results`.

**Parameters:**
- `query` (string, optional) — What to search target.com for — a product type, a brand, or a brand plus a product ('laptop', 'coffee maker', 'lego star wars', 'nike running shoes'). This runs target.com's own site search and returns its own result order. Provide `query` OR `category`. Target serves 24 products per page and this API pages through them for you up to `max_results`; `meta.pagination` reports the deepest offset Target's own pagination links publish for that query, which is how many results it will let anyone page through (1 176 for 'laptop', 912 for 'lego', 432 for 'dog food' at the time of writing). IMPORTANT, so you can plan around it: target.com NEVER returns an empty search page — a keyword it cannot match comes back as 24 unrelated recommendations with a '200 results' label. This API measures that for you and reports it in `meta.keyword_match_rate` (real keywords score 0.62-1.00, nonsense scores 0.00), and warns in `meta.notes` when the rows are recommendations rather than matches.
- `category` (string, optional) — A target.com category or brand page to browse instead of searching, given as the path of a target.com URL — `/c/cpus/-/N-01vhh`, `/c/marvel-collectibles/-/N-007on`, `/b/apple/-/N-5y3ej`. A full https://www.target.com/c/... URL is accepted too. Both forms come straight from Target's own sitemaps (6 215 category pages, 37 368 brand pages), which are the authoritative list: https://www.target.com/sitemap_taxonomy-categories-index.xml.gz and .../sitemap_taxonomy-brand-index.xml.gz. Note that some Target `/c/` URLs are landing/hub pages rather than product grids — those return zero products with `meta.notes` saying so and `related_categories` listing the real grids underneath them, rather than a misleading empty result.
- `page` (integer, optional, default 1) — Which page to start from (1-based). Target's page size is 24 and is not adjustable, so page 2 is results 25-48. Page until `meta.pagination.has_more` is false.
- `max_results` (integer, optional, default 24) — How many products to return (1-192). Target serves 24 per upstream page, so asking for more than 24 fetches more pages — each one is a further ~190 KB request against Target, so ask for what you will use.
- `sort` (enum, optional) — Result ordering, using Target's own sort tokens. Every value here is measured to change the order (`PriceLow` on 'laptop' returns $5.00-$13.49 first, `PriceHigh` returns $5 842-$3 999 first). `relevance` is Target's default, so passing it explicitly returns the same sequence as omitting `sort`. The list is closed on purpose: Target SILENTLY IGNORES a sort token it does not recognise and returns the default order, so an invalid value is rejected here rather than quietly given back to you as if it had sorted. [one of: relevance, bestselling, PriceLow, PriceHigh, RatingHigh, newest]
- `min_price` (number, optional) — Keep only products priced at or above this, in US dollars, using Target's own price filter. Verified to bite: `min_price=100&max_price=300` on 'laptop' returns only products between those bounds.
- `max_price` (number, optional) — Keep only products priced at or below this, in US dollars, using Target's own price filter. Combine with `min_price` for a band.

**Returns:** products[]{variant_tcin, product_tcin, url, position, name, brand, brand_url, category, image, price, list_price, currency, on_sale, in_stock, availability, rating, rating_count, review_count, offer_type} + related_categories[]{name, path} (the real product grids under a Target landing page, when you browsed one). TWO IDS, ON PURPOSE — `product_tcin` is the parent product page and is what you normally pass to `product_detail`; `variant_tcin` is the specific colour/size Target listed for that row. On some categories they are always different (measured: 24/24 rows on 't-shirt') and on others always the same (0/24 on 'lego'), so this API never collapses them into one 'id' — that would silently re-key your database on half the catalogue. Both are accepted by `product_detail`. meta.pagination{page, page_size, returned, offset, deepest_offset, total_results, total_results_at_most, has_more, next_page} — `total_results` is Target's OWN printed count and is null when Target does not print one (measured: it printed one on 7 of 15 sampled queries), so instead of inventing a total this API also reports the deepest offset Target's own pagination links go to and the upper bound that implies. meta.filters{applied, notes} says exactly what was sent. meta.route is 'crawler_ua' and meta.field_sources says which surface served which field. 🔴 TARGET'S SEARCH NEVER RETURNS EMPTY, so this API measures the match for you. Ask target.com for a keyword it cannot match and it answers HTTP 200 with 24 unrelated products and its own '200 results' label (and a second nonsense keyword returns 24 completely different ones), which counting rows would score as a successful search. `meta.keyword_match_rate` is the fraction of returned rows that actually contain a word from your query — 0.62-1.00 for real keywords and 0.00 for nonsense, measured over 15 queries — and below 0.15 `meta.notes` tells you in plain language that these are Target's recommendations rather than matches. HONEST LIMITS: a search row carries no barcode, no weight, no dimensions, no specifications, no variant matrix and no reviews — `product_detail` is where all of those live, and it is one call per product. Target publishes no result count and no facet vocabulary on this surface, so this API offers only the filters it can prove bite: `sort`, `min_price` and `max_price`.

**Example request body:**
```json
{
  "query": "coffee maker",
  "max_results": 24
}
```

### POST /target/v1/product_detail — 3 credits
Get one Target product in full, by Target's own id or by its target.com URL. One call returns the UPC barcode, the shipping weight and package dimensions with Target's own units, the complete image gallery, the entire colour/size variant matrix with each variant's own price, list price, stock status, barcode, weight, dimensions, image and native Target id, the specification table parsed into name/value pairs, the brand, the category breadcrumb path, the current price and availability, the star rating with its full 1-5 distribution and Target's secondary ratings, the customer review photos, and the customer reviews inline.

**Parameters:**
- `tcin` (string, optional) — Target's own product id (a 'TCIN'), exactly as `search` returns it — e.g. '87328557'. BOTH ids a search row gives you work: pass `product_tcin` (the parent product) or `variant_tcin` (one specific colour/size) and you get the same product back, because target.com resolves a variant id to its parent page. The response echoes what you asked for in `product.requested_tcin` and always reports the canonical parent in `product.tcin`. Provide `tcin`, or a product `url`.
- `url` (string, optional) — A full target.com product URL — an alternative to `tcin`. The slug is decorative: only the `A-<id>` part is read, so `/p/-/A-87328557` works too.
- `reviews` (integer, optional, default 8) — How many written customer reviews to inline with the product (0-12). This costs NO extra request — the reviews are already in the two pages this call fetches, so 0 saves you nothing but response size. The star rating, the rating count, the full 1-5 star distribution, the recommended percentage, Target's secondary ratings (comfort / quality / sizing / style, where the category has them) and the customer review photos always come back regardless of this value. Target itself only puts a limited number of review bodies on a product page, so `reviews.available_on_page` tells you how many there really were — asking for more than that is not an error, you simply get what exists.
- `include_price` (boolean, optional, default true) — Whether to fetch the price, the availability and the per-variant price/stock matrix (default true). These live on a different target.com surface from the barcode, weight, dimensions, specs, images and reviews, so getting them costs ONE extra upstream request of roughly 0.7-1.0 MB. Set false when you only need the catalogue facts — it roughly halves the bytes and the latency of this call, and everything else is unchanged. `meta.route` and `meta.field_sources` always state which surfaces actually answered.

**Returns:** product{tcin, requested_tcin, is_variant_parent, name, brand, brand_url, url, gtin13, barcode, dpci, package{weight, weight_unit, depth, height, width, dimension_unit}, price, list_price, price_range{min, max, priced_variants, of_variants, derived_from}, currency, on_sale, in_stock, availability, condition, delivery_method, description, description_html, specifications[]{name, value}, bullets[], highlights[], highlights_title, category, category_path[]{name, url, category_id}, product_type, item_type, department, import_designation, seller, return_policy, return_policy_url, images[]{url, role, alt, image_name, model_metadata}, image, image_count, varies_by[], variant_count, variants[]{tcin, name, axes{Color, Size, …}, price, list_price, currency, on_sale, in_stock, availability, condition, barcode, dpci, package{weight, weight_unit, depth, height, width, dimension_unit}, image, url, price_source, catalog_source}, variant_hierarchy[]{axis, value, tcin, swatch_image, image, children[]}, rating, rating_count, review_count, question_count, recommended_percentage, rating_distribution{1..5}, secondary_ratings[]{id, label, value, range, type, selector_text}, review_photos[], reviews{returned, available_on_page, items[]{review_id, rating, title, text, author, author_id, submitted_at, source}}}. FIELD NOTES, so nothing here is a surprise: this product is assembled from TWO target.com surfaces and `meta.field_sources` names which one gave which field. The barcode, weight, dimensions, specifications, images, breadcrumbs, rating distribution and review photos come from Target's product document; the price, the list price, the availability, the GTIN-13 and every per-variant price and stock flag come from Target's crawler render. On a product with colour/size variants the PARENT carries no barcode and no package dimensions — they are on each variant, which is why `variants[]` carries them and the top-level `barcode` can be null on a variation product while all 93 variants have one — and the same is true of `price`: target.com prices a variation product's VARIANTS, not the family, so the top-level `price` is null there and `price_range` gives the measured min/max across the variant rows (clearly marked as derived; no variant is ever given the family's price). `in_stock` is a BOOLEAN because Target publishes no stock quantity anywhere; null means Target said nothing, never zero. Each variant row says where its own data came from: `price_source` and `catalog_source`. `meta.coverage` counts how many of THIS product's variants carry a price, a stock flag, a barcode and a weight, so you can see the completeness of the rows you were given instead of trusting an average. DEGRADED MODE: if the price surface is unavailable the call still SUCCEEDS with everything the product document gives (barcode, weight, dimensions, brand, specs, images, rating, reviews, and the variant list without prices); `meta.degraded` is true and `meta.notes` says exactly which fields are missing and why. It never fails the whole call because the price leg failed. HONEST LIMITS — WHAT TARGET DOES NOT PUBLISH: no stock quantity and no per-store inventory (availability is InStock/OutOfStock only); no seller name for Target's own first-party items; and a minority of items carry no barcode and no package dimensions at all — that is category-dependent and real, so `meta.coverage` reports it per product rather than this text pretending it is always present.

**Example request body:**
```json
{
  "tcin": "87328557",
  "reviews": 5
}
```

## More
- Try it live, no code: https://reefapi.com/playground?engine=target
- Human docs page: https://reefapi.com/docs/target
- Every ReefAPI API in one file (for your AI): https://reefapi.com/llms-full.txt
