# Shopify Public Products Collector (`eichan/shopify-public-products`) Actor

Collect up to 50 product records from one user-provided Shopify store's public products.json endpoint.

- **URL**: https://apify.com/eichan/shopify-public-products.md
- **Developed by:** [Eichan Jolly](https://apify.com/eichan) (community)
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$2.00 / 1,000 results

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

## Shopify Public Products Collector

Collect a **small, bounded list of products** from one user-supplied Shopify storefront's **public** `/products.json` endpoint. This is not a general web crawler, product-variant exporter, or Shopify Admin API client.

### What it does

1. Validates one public Shopify store root `storeUrl`.
2. Requests exactly one `/products.json?limit=N&page=1` feed page from that store (where `N` defaults to **5**, maximum **50**).
3. Takes up to `N` products from the public `products` array, and emits one normalized dataset row per product with a usable ID and handle.
4. Appends the result rows to the Apify default dataset via REST. No login, API key for the store, Apify SDK, third-party Python package, or pagination loop is needed.

### Who it is for

Merchants, market researchers, and catalog operators who want a quick sample of products from a **known** Shopify storefront that voluntarily exposes its public products feed.

### Input

```json
{
  "storeUrl": "https://example-store.myshopify.com/",
  "limit": 5
}
```

The store address is **illustrative only**; it may not exist or expose products. Replace it with a real, public Shopify storefront you want to query. The Console prefill uses **one store URL** and a five-product limit.

| Field | Type | Required | Default | Meaning |
|---|---|---|---|---|
| `storeUrl` | string | Yes | — | One HTTP(S) Shopify storefront root (no path/query). |
| `limit` | integer | No | `5` | Maximum result rows requested and emitted; range 1–50. |

### Example output

An illustrative dataset item (not a claim about the sample store):

```json
{
  "product_id": "1234567890123",
  "handle": "canvas-tote-bag",
  "title": "Canvas Tote Bag",
  "vendor": "Sample Goods",
  "product_type": "Accessories",
  "product_url": "https://example-store.myshopify.com/products/canvas-tote-bag"
}
```

Each result contains exactly these six keys. Missing optional text fields become `null`. `product_id` is always a **string**, avoiding large-integer precision loss in downstream tools. Product URLs are constructed from the supplied storefront's root URL and the handle: they are *not* checked or fetched individually.

### Limits and expected behavior

- **Public Shopify feed only:** Exactly one page at `/products.json`; no store-wide crawl, variant rows, price lookup, or additional endpoints.
- **Low cost:** Default maximum 5, hard maximum 50 products; 15-second timeout; 8 MB response cap; no automatic retries.
- **Public access may vary:** A store may block, disable, redirect, password-protect, or rate-limit `/products.json`. This Actor does not circumvent access controls.
- **Response validation:** Requires a JSON object containing `products` as an array. Products missing IDs or safe handles are skipped. Empty or unusable lists create **zero dataset rows** and a plain-language log message.
- **URL restrictions:** Rejects local/private/non-global destinations at initial DNS validation, credentials, custom ports, Amazon/eBay domains, and redirects outside `/products.json` on the same store (allowing `www` hostname variants). DNS checks reduce SSRF risk but do not fully eliminate DNS rebinding.
- **No extra claims:** Some products in the JSON feed may be unpublished, removed, or non-purchasable; this Actor reports what the public feed actually provides. It does not infer availability, prices, sales, or total product count.
- **Errors:** Invalid inputs, blocked endpoints, and malformed data produce readable messages without Python tracebacks. Network or storage failures exit nonzero.
- **Public data:** Use only stores you are permitted to access and comply with the relevant website's terms and applicable law.

### Run on Apify

Upload this Actor's folder independently. `.actor/actor.json` points to `../input_schema.json` and `../Dockerfile`. Apify injects `APIFY_TOKEN` and default storage IDs. The Actor fetches its input `INPUT` record from the default key-value store and sends a JSON array to the default dataset using Apify's REST API. **No Apify SDK** is used.

### Run locally

Python 3.12+ only; no external packages:

```bash
cd shopify-public-products
printf '{"storeUrl":"https://YOUR_PUBLIC_SHOPIFY_STORE/","limit":5}\n' > input.json
python src/main.py --input input.json
```

Local mode writes **one JSONL line per product to stdout**; logs are on stderr. It does not contact Apify storage.

### Store classification and pricing suggestion

- **Apify category / industry:** E-commerce
- **Suggested use case:** Product data collection
- **Suggested output type:** Products
- **Suggested pricing:** $2.00 USD per 1,000 **successfully emitted dataset items** ($0.002 each), on a per-result basis.

These are **publishing recommendations**, not automatically configured billing or store categories. Set the current Store classification and per-result/event pricing separately when publishing. Do not bill for skipped or empty records.

# Actor input Schema

## `storeUrl` (type: `string`):

A public Shopify storefront root URL supplied by you (no path, query, or login). Replace the illustrative example before running.

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

Fetch one products.json page and output at most this many products.

## Actor input object example

```json
{
  "storeUrl": "https://example-store.myshopify.com/",
  "limit": 5
}
```

# Actor output Schema

## `dataset` (type: `string`):

Dataset containing the Actor's result items

# 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 = {
    "storeUrl": "https://www.allbirds.com/",
    "limit": 5
};

// Run the Actor and wait for it to finish
const run = await client.actor("eichan/shopify-public-products").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 = {
    "storeUrl": "https://www.allbirds.com/",
    "limit": 5,
}

# Run the Actor and wait for it to finish
run = client.actor("eichan/shopify-public-products").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 '{
  "storeUrl": "https://www.allbirds.com/",
  "limit": 5
}' |
apify call eichan/shopify-public-products --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,eichan/shopify-public-products"
        }
    }
}
```

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/knPlfIzd0TEkfJ7mc/builds/ECtk6Y2a2QmM9PePH/openapi.json
