# Target Scraper - Products, Prices and EAN Barcodes (`s-r/target-scraper`) Actor

Scrape Target.com category listings and product pages. Returns name, brand, price, availability, rating and image, and with product detail on, the EAN barcode (gtin13) and full description. Reads the JSON-LD Target publishes for search engines.

- **URL**: https://apify.com/s-r/target-scraper.md
- **Developed by:** [SR](https://apify.com/s-r) (community)
- **Categories:** E-commerce, Business
- **Stats:** 3 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 run start fees

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Target Scraper

Scrape **Target.com** category listings and product pages. Name, brand, price,
availability, rating and image from any category, and with product detail turned
on, the **EAN barcode** and the full description.

### Where the catalogue comes from

Target publishes its catalogue as JSON-LD, the structured feed the site
maintains for indexing. This Actor reads that, which is a documented, stable
structure that survives redesigns which break CSS selectors.

It also means a page arriving without that feed is an incomplete response, not
an empty category. The Actor retries and then says so explicitly, because "this
category has no products" is a confident wrong answer and the run would
otherwise look successful.

### The EAN is the reason to turn on detail

The listing gives you everything you need for a price feed: name, brand,
category, price, currency, rating, image, and the product URL. 24 per page,
paginated automatically.

The **product page** adds `gtin13`, which is the EAN barcode. That is an exact
join key against any other catalogue, so it is what lets you line Target's
assortment up against a supplier list, a competitor's prices, or the
**Open Food Facts** data in the sibling Actor.

Detail costs one extra request per product, so it is off by default. When it is
off, `gtin13` comes back `null` and the run summary says why rather than letting
you conclude the products have no barcode.

Not every product has one. Target publishes `gtin13` for most branded goods and
omits it for own-brand and some bundles. Those rows return `null`, never a
guessed value.

### Two ways in

**By category:**

```
category_url: https://www.target.com/c/laptops-home-office-electronics/-/N-5xtf4
limit: 96
```

Pagination uses Target's own `?Nao=<offset>` in steps of 24 and stops when a
page returns fewer than a full set.

**By product URL:**

```
product_urls: ["https://www.target.com/p/.../-/A-1012123457"]
```

Product URLs always fetch detail, so EAN and description are always populated on
that path.

### Fields

- **Identity**: `name`, `tcin` (Target's own id, taken from the URL),
  `identifier`, `gtin13`
- **Brand**: `brand`, `brand_url`
- **Commercial**: `price`, `currency`, `availability`
- **Social proof**: `rating`, `rating_count`, `review_count_on_page`
- **Content**: `description`, `image`, `category`
- **Provenance**: `url`, `enriched`

`enriched` tells you whether the product page was actually fetched for that row,
so you always know whether a `null` EAN means "no barcode published" or "we did
not look".

### Input reference

| Field | Type | Default |
|---|---|---|
| `category_url` | Target category page | laptops category |
| `product_urls` | list of product pages | — |
| `detail` | fetch each product page for EAN and description | `false` |
| `limit` | 1-2000 | 96 |
| `retries` | 1-6 | 3 |

Give a category URL or a list of product URLs. A non-Target URL is rejected with
a message rather than fetched.

### Typical uses

- **Price monitoring.** Run a category on a schedule and track `price` per
  `tcin`. Availability comes along with it, so you see stockouts too.
- **Assortment comparison.** Turn on detail, join on `gtin13`, and compare
  Target's range and pricing against another retailer's on exact barcode matches
  rather than fuzzy name matching.
- **Brand share of shelf.** Group a category by `brand` and count.
- **Review mining.** `rating` and `rating_count` per product, at category scale.

### Notes on behaviour

Requests are paced with a short randomised delay between listing pages and
between detail fetches. Target has not rate-limited this pattern in testing, but
the pacing is there because a listing walk plus per-product detail is a lot of
requests and hammering it would end the access for everyone.

The
crawler agent used here is the one that measured as working; if that path ever
closes it is a one-line change in `client` and the Actor will report the stub
rather than pretending the catalogue is empty.

Prices are US dollars from the US site. Target does not operate the same
catalogue outside the US, so there is no country parameter to set.

### Finding a category URL

Target category URLs look like
`https://www.target.com/c/<slug>/-/N-<code>`. The quickest way to get one is to
browse to the category in your own browser and copy the address. The `N-` code
is the category identifier and is what the Actor paginates against.

Facets work too. Target encodes filters into the same path, so a URL like
`.../c/laptops-home-office-electronics/sale/-/N-5xtf4Z5tdv0` scrapes only the
sale items in that category, and the Actor handles it exactly like any other
listing. That is usually easier than filtering the output afterwards, because
the facet is applied before pagination and you spend fewer requests.

### What this Actor does not do

**No search results.** Target's search pages behave differently from category
pages and did not yield the same JSON-LD in testing. Use a category or a facet
URL instead.

**No stock levels per store.** Availability comes back as `InStock` or
`OutOfStock` for the online catalogue. Target does publish per-store inventory
in its own app, but not in the page data this Actor reads, and inventing a
number there would be worse than returning nothing.

**No historical prices.** Every run is a snapshot. Schedule the Actor and keep
the runs if you want a price history; the `tcin` field is stable and is the key
to join snapshots on.

# Actor input Schema

## `category_url` (type: `string`):

A Target category page, for example https://www.target.com/c/laptops-home-office-electronics/-/N-5xtf4. Paginated automatically, 24 products per page.

## `product_urls` (type: `array`):

Specific Target product pages to scrape instead of a category. These always return the EAN and description.

## `detail` (type: `boolean`):

Fetch each product's own page to add the EAN barcode, the full description and live availability. One extra request per product, so it is slower.

## `limit` (type: `integer`):

How many products to return. Listings come 24 at a time.

## `retries` (type: `integer`):

Retries with backoff before a request is reported as an error.

## Actor input object example

```json
{
  "category_url": "https://www.target.com/c/laptops-home-office-electronics/-/N-5xtf4",
  "detail": false,
  "limit": 96,
  "retries": 3
}
```

# Actor output Schema

## `products` (type: `string`):

One row per product.

## `summary` (type: `string`):

Counts, price coverage and how many rows carry an EAN.

## `errors` (type: `string`):

Failures with a code and a redacted message.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "category_url": "https://www.target.com/c/laptops-home-office-electronics/-/N-5xtf4",
    "limit": 96,
    "retries": 3
};

// Run the Actor and wait for it to finish
const run = await client.actor("s-r/target-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "category_url": "https://www.target.com/c/laptops-home-office-electronics/-/N-5xtf4",
    "limit": 96,
    "retries": 3,
}

# Run the Actor and wait for it to finish
run = client.actor("s-r/target-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "category_url": "https://www.target.com/c/laptops-home-office-electronics/-/N-5xtf4",
  "limit": 96,
  "retries": 3
}' |
apify call s-r/target-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,s-r/target-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/HmaJTswU4x8xiiYyt/builds/YOftbOVqaZZ3tknVk/openapi.json
