---
title: "Get a Home Depot product by item id or URL"
method: GET
path: "/home_depot/product"
tags: ["home_depot"]
---

# Get a Home Depot product by item id or URL

`GET /home_depot/product`

## Request

- Base URL: `https://www.socialcrawl.dev/v1`
- URL: `https://www.socialcrawl.dev/v1/home_depot/product`
- Auth: API key in header `x-api-key`

Returns full product detail for a Home Depot item: title, brand, model number, UPC, description, highlight bullets, current and original price, star rating, review count, the image gallery, and stock status. It also returns inventory under product.ext.store_inventory, giving the actual unit count at each location that can fulfil the item, which is the field nothing else in this API sells. Home Depot lists one location per fulfilment path, so a row is not always a store: each row carries fulfillment (pickup or delivery), service (for example bopis, express delivery or sth), location_type (store or online), and is_selected_store. One row has is_selected_store true, and that row is the store the request was localised to, echoing the store_id you sent in the same format. Read that flag rather than matching store ids numerically, because a delivery node can share digits with a store number (0121 is not store 121). On the rare response where the flag is null for every row, the selected store is the pickup row whose store_id equals the store_id you sent, compared as an exact string. Pass store_id to localise that inventory; without it Home Depot picks a default store. Prices are Home Depot's online price and did not vary by store in any comparison we have run (four stores in three states on 28/09/2026), so in-store-only shelf markdowns are not in this data. A markdown Home Depot publishes online appears under product.ext.promotion with its label (Clearance or Special Buys), amount_off and percent_off, and price.original is the price before that markdown. When Home Depot withholds a price, price.current is null and product.ext.price_note says why, for example see final price in cart. Get a store id from GET /v1/home_depot/stores. For the review text call GET /v1/home_depot/reviews with the same item id.

## Query parameters

- `item_id` string
- `url` string
- `store_id` string

## Headers

- `Cache-Control` string
- `Idempotency-Key` string

## Response `200`

Successful response

- object
  - `success` boolean, required: Whether the request succeeded
  - `platform` string, required: Platform name
  - `endpoint` string, required: API endpoint path
  - `data` object, required: Platform-specific response data
    - `id` string: Platform product ID (Amazon ASIN / Google Shopping product id)
    - `url` string, nullable: Direct URL to the product page
    - `title` string, nullable: Product title
    - `description` string, nullable: String at product.description
    - `seller` string, nullable: String at product.seller
    - `brand` string, nullable: Brand name (cleaned). Null when the platform exposes a seller instead.
    - `price` object: Price block (current, original/list, currency)
      - `current` integer, nullable: Numeric at product.price.current
      - `original` integer, nullable: Numeric at product.price.original
      - `currency` string, nullable: String at product.price.currency
    - `rating` object: Aggregate rating (average + number of ratings)
      - `average` integer, nullable: Numeric at product.rating.average
      - `count` integer, nullable: Numeric at product.rating.count
    - `image_urls` union: Primary image URL, or an array of image URLs for products with a gallery.
      - string
      - string[]
    - `availability` string, nullable: Stock/availability string when surfaced
    - `reviews_count` integer, nullable: Numeric at product.reviews_count
    - `features` string[], nullable: Array at product.features
    - `specifications` object[], nullable: Array at product.specifications
      - `group` string, nullable: String at product.specifications.group
      - `name` string, nullable: String at product.specifications.name
      - `value` string, nullable: String at product.specifications.value
    - `variations` object[], nullable: Array at product.variations
      - `id` string, nullable: String at product.variations.id
      - `title` string, nullable: String at product.variations.title
      - `url` string, nullable: String at product.variations.url
      - `category` string, nullable: String at product.variations.category
    - `ext` object, nullable: Nested object: product.ext
      - `gid` string, nullable: String at product.ext.gid
      - `data_docid` string, nullable: String at product.ext.data_docid
      - `pvf` string, nullable: String at product.ext.pvf
      - `seller_id` string, nullable: String at product.ext.seller_id
      - `sold_count` integer, nullable: Numeric at product.ext.sold_count
      - `bought_past_month` integer, nullable: Numeric at product.ext.bought_past_month
      - `bought_past_month_label` string, nullable: String at product.ext.bought_past_month_label
      - `catalog_id` string, nullable: String at product.ext.catalog_id
      - `requested_id` string, nullable: String at product.ext.requested_id
      - `rating_distribution` object, nullable: Nested object: product.ext.rating_distribution
        - `star_1` integer, nullable: Numeric at product.ext.rating_distribution.star_1
        - `star_2` integer, nullable: Numeric at product.ext.rating_distribution.star_2
        - `star_3` integer, nullable: Numeric at product.ext.rating_distribution.star_3
        - `star_4` integer, nullable: Numeric at product.ext.rating_distribution.star_4
        - `star_5` integer, nullable: Numeric at product.ext.rating_distribution.star_5
      - `store_inventory` object[], nullable: Array at product.ext.store_inventory
        - `store_id` string, nullable: String at product.ext.store_inventory.store_id
        - `store_name` string, nullable: String at product.ext.store_inventory.store_name
        - `state` string, nullable: String at product.ext.store_inventory.state
        - `in_stock` boolean, nullable: Boolean at product.ext.store_inventory.in_stock
        - `quantity` integer, nullable: Numeric at product.ext.store_inventory.quantity
        - `fulfillment` string, nullable: String at product.ext.store_inventory.fulfillment
        - `service` string, nullable: String at product.ext.store_inventory.service
        - `location_type` string, nullable: String at product.ext.store_inventory.location_type
        - `is_selected_store` boolean, nullable: Boolean at product.ext.store_inventory.is_selected_store
      - `promotion` object, nullable: Nested object: product.ext.promotion
        - `label` string, nullable: String at product.ext.promotion.label
        - `amount_off` integer, nullable: Numeric at product.ext.promotion.amount_off
        - `percent_off` integer, nullable: Numeric at product.ext.promotion.percent_off
      - `price_note` string, nullable: String at product.ext.price_note
      - `condition` string, nullable: String at product.ext.condition
      - `available_quantity` integer, nullable: Numeric at product.ext.available_quantity
      - `watchers` integer, nullable: Numeric at product.ext.watchers
      - `sold_at` string, nullable: String at product.ext.sold_at
      - `sold_caption` string, nullable: String at product.ext.sold_caption
      - `buying_format` string, nullable: String at product.ext.buying_format
      - `seller_reputation` object, nullable: Nested object: product.ext.seller_reputation
        - `feedback_percentage` integer, nullable: Numeric at product.ext.seller_reputation.feedback_percentage
        - `feedback_count` integer, nullable: Numeric at product.ext.seller_reputation.feedback_count
        - `top_rated` boolean, nullable: Boolean at product.ext.seller_reputation.top_rated
        - `items_sold` integer, nullable: Numeric at product.ext.seller_reputation.items_sold
        - `joined` string, nullable: String at product.ext.seller_reputation.joined
        - `url` string, nullable: String at product.ext.seller_reputation.url
        - `detailed_ratings` object, nullable: Nested object: product.ext.seller_reputation.detailed_ratings
          - `accurate_description` integer, nullable: Numeric at product.ext.seller_reputation.detailed_ratings.accurate_description
          - `reasonable_shipping_cost` integer, nullable: Numeric at product.ext.seller_reputation.detailed_ratings.reasonable_shipping_cost
          - `shipping_speed` integer, nullable: Numeric at product.ext.seller_reputation.detailed_ratings.shipping_speed
          - `communication` integer, nullable: Numeric at product.ext.seller_reputation.detailed_ratings.communication
      - `g2` string, nullable: Leaf at product.ext.g2
      - `etsy` string, nullable: Leaf at product.ext.etsy
      - `sephora` string, nullable: Leaf at product.ext.sephora
      - `hm` string, nullable: Leaf at product.ext.hm
      - `kohls` string, nullable: Leaf at product.ext.kohls
      - `gumtree` string, nullable: Leaf at product.ext.gumtree
      - `sku_id` string, nullable: String at product.ext.sku_id
      - `aliexpress` string, nullable: Leaf at product.ext.aliexpress
      - `tiktokshop` string, nullable: Leaf at product.ext.tiktokshop
    - `_warnings` string[]: Non-fatal notices about this response (field-map drift, clamped computed values). Advisory only: its presence never means the request failed. Omitted entirely when there is nothing to report, so treat absent as 'no warnings'.
  - `credits_used` integer, required: Number of credits consumed
  - `credits_remaining` integer, nullable, required: Current account balance, or null when this response could not establish one. Free cache hits always return null because they do not establish an authoritative post-request balance. An idempotency replay also returns null when its current-balance lookup fails. It is never a fabricated zero, so a zero here means the account really is empty. The authoritative reading is the one on a billed response, which comes straight from the deduction.
  - `request_id` string, required: Unique request identifier for support
  - `cached` boolean, required: Whether the response was served from cache
  - `idempotent_replay` boolean: True only when this response is an idempotency replay
  - `meta` object: Optional. Present only when this call completed a pattern that one other call does more cheaply or in one step, at most once per pattern per API key per day. Send the header `X-SocialCrawl-Hints: off` to never receive it.
    - `hint` object: One suggestion: what to call instead and why, with the price when the suggested call costs more than the calls it replaces.
      - `code` string: creator_card | batch_profiles | search_multi | incremental_sync | comment_labels | search_repeat
      - `message` string: One sentence for a person or an agent to read.
      - `method` string: GET or POST.
      - `path` string: The call the hint points to.

## Other responses

- `400`: Invalid request - missing or malformed parameters
- `401`: Unauthorized - missing or invalid API key
- `402`: Payment required - the account balance is too low, or the calling key has spent its own per-key credit limit
- `404`: Not found - the endpoint does not exist, or the requested resource was not found upstream
- `405`: Method not allowed - wrong HTTP verb for this endpoint
- `409`: Conflict - an idempotent request is still in flight, its completed body exceeded the replay limit, or the cohort resource is not in a state that allows this operation
- `410`: Gone - the discover run's retention passed and its rows were deleted; start a new run
- `413`: Payload too large: the JSON request body exceeds the 1 MB size limit and is rejected before parsing, or a single cohort result cannot fit beneath the 1 MB response-page ceiling
- `422`: Idempotency payload mismatch - this Idempotency-Key was already used with a different request payload; or a discover request with async=false is over what one call can answer (NEEDS_ASYNC)
- `429`: Rate or concurrency limit exceeded. `RATE_LIMITED`: more than 600 requests in a 1-minute sliding window on this API key (headers `X-RateLimit-Limit`/`Remaining`/`Reset`; `Retry-After` is seconds until the window resets). `CONCURRENCY_LIMIT`: more than 50 simultaneous in-flight requests (headers `X-Concurrency-Limit`/`Remaining`; short static `Retry-After`). Both are unbilled. Honor `Retry-After`, then back off with jitter. See /docs/rate-limits.
- `500`: Internal server error - an immediate refund is attempted; credits_used reports the committed net charge and exact compensation is queued if that refund cannot commit
- `502`: Upstream error - the platform returned an error; an immediate refund is attempted and credits_used reports the committed net charge
- `503`: Service unavailable - the request is unbilled when rejected before deduction, otherwise an immediate refund is attempted and credits_used reports the committed net charge
- `504`: Gateway timeout - the request was still running close to the platform time limit and is answered as UPSTREAM_ERROR, the same code as 502. An immediate refund is attempted and credits_used reports the committed net charge. When details.retry_with is set, retryable is false and there is no Retry-After

---

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