Structured data
Popular page types as typed JSON: Reddit posts, search and comments, and Bing and DuckDuckGo results.
Popular page types as typed JSON: Reddit posts, search and comments, and Bing and DuckDuckGo results.
Endpoints
Method | Path | Does |
|---|---|---|
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
|
How it works, when to use what, and what it costs: Reddit and web search. Base URL, authentication and errors: Overview.
GET /v1/structured
List structured endpoints.
Every structured endpoint with its parameters and status (live or coming_soon).
Request
- cURL
- Python
- Node.js
curl "https://api.scrapelyserp.com/v1/structured" \
-H "Authorization: Bearer $SCRAPELY_API_KEY"import os
import requests
HEADERS = {"Authorization": f"Bearer {os.environ['SCRAPELY_API_KEY']}"}
r = requests.get(
"https://api.scrapelyserp.com/v1/structured",
headers=HEADERS,
timeout=30,
)
r.raise_for_status()
data = r.json()const res = await fetch('https://api.scrapelyserp.com/v1/structured', {
headers: { Authorization: `Bearer ${process.env.SCRAPELY_API_KEY}` },
signal: AbortSignal.timeout(30_000),
});
if (!res.ok) throw new Error((await res.json()).error.code);
const data = await res.json();Response
{
"object": "list",
"data": [
{
"id": "reddit/posts",
"site": "reddit",
"name": "Reddit posts",
"description": "The posts of a subreddit, newest or top first.",
"status": "live",
"coming_soon": false,
"url": "https://api.scrapelyserp.com/api/v1/structured/reddit/posts",
"params": [
{
"name": "subreddit",
"type": "string",
"required": true,
"default": null,
"values": null,
"description": "The subreddit, with or without r/."
}
],
"example": {
"subreddit": "webscraping",
"sort": "new"
}
}
]
}Responses
Status | Meaning |
|---|---|
| Every structured endpoint with its parameters. |
|
|
|
|
|
|
Response fields
Field | Type | Description |
|---|---|---|
| "list" | — |
| object[] | — |
GET /v1/structured/amazon/search
Amazon search.
Amazon search results for a query, in Amazon's order: ASIN, title, price and list price, rating, review count, image and whether the placement is sponsored. 16 to 60 per page (Amazon picks the layout for each query); send next_page back as page for more, up to page 20. sort is best_match, best_selling, price_low, price_high, rating or newest. Sponsored placements are included and marked sponsored: true. price is the price the result shows (null when it shows none) and list_price the struck-through list or typical price when there is one. brand and in_stock are always null: search does not carry them, ask POST /v1/products. Each id (and asin) is what POST /v1/products takes. A keyword that matches nothing is an empty list (total_results 0), and so is a page past the last one; an answer we cannot read is a failure (502 TARGET_FAILED, not charged, retry it), not an empty list. Amazon US.
Cost: charged credits for the data it moves, like every endpoint. Also accepts POST with a JSON body.
Query parameters
Name | Type | Required | Description |
|---|---|---|---|
| string | yes | The search terms. |
| integer | — | Results page, 1–20 (Amazon shows at most 20). Default |
| string | — | Result order. Each site offers only the orders it supports. One of: |
Request
- cURL
- Python
- Node.js
curl -G "https://api.scrapelyserp.com/v1/structured/amazon/search" \
-H "Authorization: Bearer $SCRAPELY_API_KEY" \
--data-urlencode "query=wireless earbuds"import os
import requests
HEADERS = {"Authorization": f"Bearer {os.environ['SCRAPELY_API_KEY']}"}
r = requests.get(
"https://api.scrapelyserp.com/v1/structured/amazon/search",
headers=HEADERS,
params={"query": "wireless earbuds"},
timeout=30,
)
r.raise_for_status()
data = r.json()const res = await fetch(`https://api.scrapelyserp.com/v1/structured/amazon/search?${new URLSearchParams({ query: 'wireless earbuds' })}`, {
headers: { Authorization: `Bearer ${process.env.SCRAPELY_API_KEY}` },
signal: AbortSignal.timeout(30_000),
});
if (!res.ok) throw new Error((await res.json()).error.code);
const data = await res.json();Response
{
"endpoint": "amazon/search",
"url": "https://www.amazon.com/s?k=wireless%20earbuds",
"credits": 0.000028,
"duration_ms": 1480,
"data": {
"query": "wireless earbuds",
"page": 1,
"total_results": 29095,
"next_page": 2,
"results": [
{
"position": 3,
"id": "B0BQPNMXQV",
"asin": "B0BQPNMXQV",
"title": "JBL Vibe Beam - True Wireless Earbuds - Black",
"url": "https://www.amazon.com/dp/B0BQPNMXQV",
"brand": null,
"image": "https://m.media-amazon.com/images/I/41+1Csr1pSL._AC_UY218_.jpg",
"price": {
"amount": 29.95,
"currency": "USD"
},
"list_price": {
"amount": 49.95,
"currency": "USD"
},
"rating": 4.2,
"reviews_count": 41401,
"in_stock": null,
"sponsored": false
}
]
}
}Responses
Status | Meaning |
|---|---|
| The amazon search as JSON. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Response fields
Field | Type | Description |
|---|---|---|
| string | — |
| string | The page we fetched. |
| number | What the call was charged, in credits, fractional (also |
| integer | — |
| object | — |
| string | — |
| integer | — |
| integer or null | — |
| integer or null | Send as |
| string or null | Poshmark only: send as |
| object[] | — |
GET /v1/structured/amazon/image-search
Amazon image search.
Amazon products that look like a photo: the objects found in it, ranked ASIN matches with a score each, and title, price, list price, rating, reviews, stock, brand and variations for the top matches. Send an image URL, up to 10 image URLs, or the image itself in base64 with POST. JPEG and PNG.
Cost: charged credits for the data it moves, like every endpoint. Also accepts POST with a JSON body.
Query parameters
Name | Type | Required | Description |
|---|---|---|---|
| string | — | A public http or https URL of a JPEG or PNG photo. |
| string | — | Up to 10 image URLs: a JSON array, or comma-separated. Each is answered on its own, in the order sent. |
| string | — | POST only: the image itself, base64 (a |
Request
- cURL
- Python
- Node.js
curl -G "https://api.scrapelyserp.com/v1/structured/amazon/image-search" \
-H "Authorization: Bearer $SCRAPELY_API_KEY" \
--data-urlencode "image_url=https://m.media-amazon.com/images/I/61PQSiTkywL.jpg"import os
import requests
HEADERS = {"Authorization": f"Bearer {os.environ['SCRAPELY_API_KEY']}"}
r = requests.get(
"https://api.scrapelyserp.com/v1/structured/amazon/image-search",
headers=HEADERS,
params={"image_url": "https://m.media-amazon.com/images/I/61PQSiTkywL.jpg"},
timeout=30,
)
r.raise_for_status()
data = r.json()const res = await fetch(`https://api.scrapelyserp.com/v1/structured/amazon/image-search?${new URLSearchParams({ image_url: 'https://m.media-amazon.com/images/I/61PQSiTkywL.jpg' })}`, {
headers: { Authorization: `Bearer ${process.env.SCRAPELY_API_KEY}` },
signal: AbortSignal.timeout(30_000),
});
if (!res.ok) throw new Error((await res.json()).error.code);
const data = await res.json();Response
{
"endpoint": "amazon/image-search",
"url": "https://www.amazon.com/",
"credits": 0.000028,
"duration_ms": 1480,
"data": {
"results": [
{
"image_url": "https://m.media-amazon.com/images/I/61PQSiTkywL.jpg",
"status": "ok",
"objects": [
{
"label": "footwear",
"category": "shoes",
"score": 0.9655,
"box": {
"x": 7,
"y": 158,
"width": 787,
"height": 369,
"image_width": 800,
"image_height": 571
}
}
],
"matches": [
{
"rank": 1,
"asin": "B08Q9SDCFJ",
"score": 0.9158,
"kind": "visual",
"object": 0
},
{
"rank": 2,
"asin": "B08QB1QMJ5",
"score": 0.8803,
"kind": "visual",
"object": 0
}
],
"products": [
{
"asin": "B08Q9SDCFJ",
"title": "Nike Women's Dunk Low Sneaker",
"url": "https://www.amazon.com/dp/B08Q9SDCFJ",
"brand": "Nike",
"image": "https://m.media-amazon.com/images/I/61PQSiTkywL.jpg",
"price": {
"amount": 99.95,
"currency": "USD"
},
"list_price": {
"amount": 120,
"currency": "USD"
},
"rating": 4.6,
"reviews_count": 3720,
"availability": "in_stock",
"in_stock": true,
"variations": ["B0BKR4NWW6", "B0DC6Z3CXX"],
"colors": []
}
],
"queries": []
}
]
}
}Responses
Status | Meaning |
|---|---|
| The amazon image search as JSON. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Response fields
Field | Type | Description |
|---|---|---|
| string | — |
| string | The page we fetched. |
| number | What the call was charged, in credits, fractional (also |
| integer | — |
| object | — |
| object[] | One per image, in the order sent. |
GET /v1/structured/amazon/deals
Amazon deals.
Today's Amazon deals, in Amazon's order: ASIN, title, deal price and list price, percent off, deal type and badge, end time for lightning deals, rating, reviews, brand and variation count. Filter by department, brand, rating or Prime Exclusive; up to 100 deals per page.
Cost: charged credits for the data it moves, like every endpoint. Also accepts POST with a JSON body.
Query parameters
Name | Type | Required | Description |
|---|---|---|---|
| string | — | Only deals in one department: our slug (e.g. |
| string | — | Only deals of one brand: its id from |
| string | — |
|
| string | — |
|
| integer | — | Page, 1–100. The list runs to about 500 deals; |
| integer | — | Deals per page, 1–100. Pages are |
| boolean | — | Also return |
Request
- cURL
- Python
- Node.js
curl -G "https://api.scrapelyserp.com/v1/structured/amazon/deals" \
-H "Authorization: Bearer $SCRAPELY_API_KEY" \
-d department=electronics \
-d limit=20import os
import requests
HEADERS = {"Authorization": f"Bearer {os.environ['SCRAPELY_API_KEY']}"}
r = requests.get(
"https://api.scrapelyserp.com/v1/structured/amazon/deals",
headers=HEADERS,
params={"department": "electronics", "limit": "20"},
timeout=30,
)
r.raise_for_status()
data = r.json()const res = await fetch(`https://api.scrapelyserp.com/v1/structured/amazon/deals?${new URLSearchParams({ department: 'electronics', limit: '20' })}`, {
headers: { Authorization: `Bearer ${process.env.SCRAPELY_API_KEY}` },
signal: AbortSignal.timeout(30_000),
});
if (!res.ok) throw new Error((await res.json()).error.code);
const data = await res.json();Response
{
"endpoint": "amazon/deals",
"url": "https://www.amazon.com/deals",
"credits": 0.000028,
"duration_ms": 1480,
"data": {
"page": 1,
"limit": 2,
"next_page": 2,
"deals": [
{
"position": 1,
"asin": "B0F7Z4QZTT",
"title": "Amazon Fire TV Stick 4K Plus with AI-powered Fire TV Search, Wi-Fi 6",
"url": "https://www.amazon.com/dp/B0F7Z4QZTT",
"image": "https://m.media-amazon.com/images/I/51WtNy0OxLL.jpg",
"brand": "Amazon",
"brand_id": "323125",
"price": {
"amount": 39.99,
"currency": "USD"
},
"list_price": {
"amount": 69.99,
"currency": "USD"
},
"percent_off": 43,
"deal_type": "best_deal",
"badge": "Limited time deal",
"deal_state": "available",
"ends_at": null,
"claimed_percent": null,
"rating": 4.6,
"reviews_count": 116169,
"variations": 1
},
{
"position": 2,
"asin": "B0C1NQZDYH",
"title": "BEDLORE Waterproof Mattress Protector, King Size Mattress Pad",
"url": "https://www.amazon.com/dp/B0C1NQZDYH",
"image": "https://m.media-amazon.com/images/I/81X+MBrUg+L.jpg",
"brand": "BEDLORE",
"brand_id": "4742140",
"price": {
"amount": 26.38,
"currency": "USD"
},
"list_price": {
"amount": 32.99,
"currency": "USD"
},
"percent_off": 20,
"deal_type": "lightning_deal",
"badge": "Ends in",
"deal_state": "available",
"ends_at": "2026-11-06T05:50:00.000Z",
"claimed_percent": 32,
"rating": 4.6,
"reviews_count": 29101,
"variations": 61
}
]
}
}Responses
Status | Meaning |
|---|---|
| The amazon deals as JSON. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Response fields
Field | Type | Description |
|---|---|---|
| string | — |
| string | The page we fetched. |
| number | What the call was charged, in credits, fractional (also |
| integer | — |
| object | — |
| integer | — |
| integer | — |
| integer or null | Send as |
| object[] | In Amazon's order. Empty past the end of the list. |
| object | With |
GET /v1/structured/walmart/search
Walmart search.
Walmart search results for a query, in the site's order: item id, title, brand, price and list price, rating, review count, stock and image. 25 per page; send next_page back as page for more. Each id is what POST /v1/products takes for Walmart.
Cost: charged credits for the data it moves, like every endpoint. Also accepts POST with a JSON body.
Query parameters
Name | Type | Required | Description |
|---|---|---|---|
| string | yes | The search terms. |
| integer | — | Results page, 1–100. Default |
| string | — | Result order. Each site offers only the orders it supports. One of: |
Request
- cURL
- Python
- Node.js
curl -G "https://api.scrapelyserp.com/v1/structured/walmart/search" \
-H "Authorization: Bearer $SCRAPELY_API_KEY" \
--data-urlencode "query=air fryer"import os
import requests
HEADERS = {"Authorization": f"Bearer {os.environ['SCRAPELY_API_KEY']}"}
r = requests.get(
"https://api.scrapelyserp.com/v1/structured/walmart/search",
headers=HEADERS,
params={"query": "air fryer"},
timeout=30,
)
r.raise_for_status()
data = r.json()const res = await fetch(`https://api.scrapelyserp.com/v1/structured/walmart/search?${new URLSearchParams({ query: 'air fryer' })}`, {
headers: { Authorization: `Bearer ${process.env.SCRAPELY_API_KEY}` },
signal: AbortSignal.timeout(30_000),
});
if (!res.ok) throw new Error((await res.json()).error.code);
const data = await res.json();Response
{
"endpoint": "walmart/search",
"url": "https://www.walmart.com/search?q=air%20fryer",
"credits": 0.000028,
"duration_ms": 1480,
"data": {
"query": "air fryer",
"page": 1,
"total_results": 2498,
"next_page": 2,
"results": [
{
"position": 1,
"id": "16101019328",
"title": "Beautiful by Drew Barrymore 6 qt Air Fryer with TurboCrisp Technology, White Icing",
"url": "https://www.walmart.com/ip/16101019328",
"brand": "Beautiful",
"image": null,
"price": {
"amount": 59,
"currency": "USD"
},
"list_price": {
"amount": 69,
"currency": "USD"
},
"rating": 4.6,
"reviews_count": 20544,
"in_stock": true,
"sponsored": false
}
]
}
}Responses
Status | Meaning |
|---|---|
| The walmart search as JSON. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Response fields
Field | Type | Description |
|---|---|---|
| string | — |
| string | The page we fetched. |
| number | What the call was charged, in credits, fractional (also |
| integer | — |
| object | — |
| string | — |
| integer | — |
| integer or null | — |
| integer or null | Send as |
| string or null | Poshmark only: send as |
| object[] | — |
GET /v1/structured/target/search
Target search.
Target search results for a query, in the site's order: TCIN, title, brand, price and regular price, rating, review count and image. 24 per page; send next_page back as page for more. Each id is what POST /v1/products takes for Target.
Cost: charged credits for the data it moves, like every endpoint. Also accepts POST with a JSON body.
Query parameters
Name | Type | Required | Description |
|---|---|---|---|
| string | yes | The search terms. |
| integer | — | Results page, 1–100. Default |
| string | — | Result order. Each site offers only the orders it supports. One of: |
| string | — | A 5-digit US ZIP code. Results reflect that area (local prices, stock and nearby results). Unknown ZIP codes are a free 400. |
Request
- cURL
- Python
- Node.js
curl -G "https://api.scrapelyserp.com/v1/structured/target/search" \
-H "Authorization: Bearer $SCRAPELY_API_KEY" \
--data-urlencode "query=air fryer"import os
import requests
HEADERS = {"Authorization": f"Bearer {os.environ['SCRAPELY_API_KEY']}"}
r = requests.get(
"https://api.scrapelyserp.com/v1/structured/target/search",
headers=HEADERS,
params={"query": "air fryer"},
timeout=30,
)
r.raise_for_status()
data = r.json()const res = await fetch(`https://api.scrapelyserp.com/v1/structured/target/search?${new URLSearchParams({ query: 'air fryer' })}`, {
headers: { Authorization: `Bearer ${process.env.SCRAPELY_API_KEY}` },
signal: AbortSignal.timeout(30_000),
});
if (!res.ok) throw new Error((await res.json()).error.code);
const data = await res.json();Response
{
"endpoint": "target/search",
"url": "https://www.target.com/s?searchTerm=air%20fryer",
"credits": 0.000028,
"duration_ms": 1480,
"data": {
"query": "air fryer",
"page": 1,
"total_results": 272,
"next_page": 2,
"results": [
{
"position": 1,
"id": "89110863",
"title": "Gourmia 6qt Digital Window Air Fryer with 12 Presets & Guided Cooking Black",
"url": "https://www.target.com/p/-/A-89110863",
"brand": "Gourmia",
"image": null,
"price": {
"amount": 44.99,
"currency": "USD"
},
"list_price": {
"amount": 69.99,
"currency": "USD"
},
"rating": 4.07,
"reviews_count": 2309,
"in_stock": null,
"sponsored": false
}
]
}
}Responses
Status | Meaning |
|---|---|
| The target search as JSON. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Response fields
Field | Type | Description |
|---|---|---|
| string | — |
| string | The page we fetched. |
| number | What the call was charged, in credits, fractional (also |
| integer | — |
| object | — |
| string | — |
| integer | — |
| integer or null | — |
| integer or null | Send as |
| string or null | Poshmark only: send as |
| object[] | — |
GET /v1/structured/bestbuy/search
Best Buy search.
Best Buy search results for a query, in the site's order: SKU, title, brand, price and regular price, rating, review count and image. 50 per page; send next_page back as page for more. Each id is what POST /v1/products takes for Best Buy.
Cost: charged credits for the data it moves, like every endpoint. Also accepts POST with a JSON body.
Query parameters
Name | Type | Required | Description |
|---|---|---|---|
| string | yes | The search terms. |
| integer | — | Results page, 1–100. Default |
| string | — | Result order. Each site offers only the orders it supports. One of: |
Request
- cURL
- Python
- Node.js
curl -G "https://api.scrapelyserp.com/v1/structured/bestbuy/search" \
-H "Authorization: Bearer $SCRAPELY_API_KEY" \
--data-urlencode "query=air fryer"import os
import requests
HEADERS = {"Authorization": f"Bearer {os.environ['SCRAPELY_API_KEY']}"}
r = requests.get(
"https://api.scrapelyserp.com/v1/structured/bestbuy/search",
headers=HEADERS,
params={"query": "air fryer"},
timeout=30,
)
r.raise_for_status()
data = r.json()const res = await fetch(`https://api.scrapelyserp.com/v1/structured/bestbuy/search?${new URLSearchParams({ query: 'air fryer' })}`, {
headers: { Authorization: `Bearer ${process.env.SCRAPELY_API_KEY}` },
signal: AbortSignal.timeout(30_000),
});
if (!res.ok) throw new Error((await res.json()).error.code);
const data = await res.json();Response
{
"endpoint": "bestbuy/search",
"url": "https://www.bestbuy.com/site/searchpage.jsp?st=air%20fryer",
"credits": 0.000028,
"duration_ms": 1480,
"data": {
"query": "air fryer",
"page": 1,
"total_results": 66,
"next_page": 2,
"results": [
{
"position": 1,
"id": "6583884",
"title": "bella PRO - SmartCrisp 8-qt. Touchscreen Air Fryer - Stainless Steel",
"url": "https://www.bestbuy.com/site/6583884.p?skuId=6583884",
"brand": "bella PRO",
"image": null,
"price": {
"amount": 59.99,
"currency": "USD"
},
"list_price": {
"amount": 129.99,
"currency": "USD"
},
"rating": 4.6,
"reviews_count": 1840,
"in_stock": null,
"sponsored": false
}
]
}
}Responses
Status | Meaning |
|---|---|
| The best buy search as JSON. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Response fields
Field | Type | Description |
|---|---|---|
| string | — |
| string | The page we fetched. |
| number | What the call was charged, in credits, fractional (also |
| integer | — |
| object | — |
| string | — |
| integer | — |
| integer or null | — |
| integer or null | Send as |
| string or null | Poshmark only: send as |
| object[] | — |
GET /v1/structured/petsmart/search
PetSmart search.
PetSmart search results for a query, in the site's order: product id, title, brand, lowest and highest price, rating, review count, stock and image. 40 per page; send next_page back as page for more. Each id is what POST /v1/products takes for PetSmart.
Cost: charged credits for the data it moves, like every endpoint. Also accepts POST with a JSON body.
Query parameters
Name | Type | Required | Description |
|---|---|---|---|
| string | yes | The search terms. |
| integer | — | Results page, 1–100. Default |
| string | — | Result order. Each site offers only the orders it supports. One of: |
Request
- cURL
- Python
- Node.js
curl -G "https://api.scrapelyserp.com/v1/structured/petsmart/search" \
-H "Authorization: Bearer $SCRAPELY_API_KEY" \
--data-urlencode "query=dog food"import os
import requests
HEADERS = {"Authorization": f"Bearer {os.environ['SCRAPELY_API_KEY']}"}
r = requests.get(
"https://api.scrapelyserp.com/v1/structured/petsmart/search",
headers=HEADERS,
params={"query": "dog food"},
timeout=30,
)
r.raise_for_status()
data = r.json()const res = await fetch(`https://api.scrapelyserp.com/v1/structured/petsmart/search?${new URLSearchParams({ query: 'dog food' })}`, {
headers: { Authorization: `Bearer ${process.env.SCRAPELY_API_KEY}` },
signal: AbortSignal.timeout(30_000),
});
if (!res.ok) throw new Error((await res.json()).error.code);
const data = await res.json();Response
{
"endpoint": "petsmart/search",
"url": "https://www.petsmart.com/search?q=dog%20food",
"credits": 0.000028,
"duration_ms": 1480,
"data": {
"query": "dog food",
"page": 1,
"total_results": 4034,
"next_page": 2,
"results": [
{
"position": 1,
"id": "36648",
"title": "Purina Pro Plan Sensitive Skin and Stomach Dry Dog Food Adult Salmon & Rice Formula Digestive Health",
"url": "https://www.petsmart.com/dog/food/dry-food/purina-pro-plan-sensitive-skin-and-stomach-dry-dog-food-adult-salmon-and-rice-formula-digestive-health-36648.html",
"brand": "Purina Pro Plan",
"image": null,
"price": {
"amount": 20.68,
"currency": "USD"
},
"price_max": {
"amount": 94.99,
"currency": "USD"
},
"list_price": null,
"rating": 4.5,
"reviews_count": 9248,
"in_stock": true,
"sponsored": false
}
]
}
}Responses
Status | Meaning |
|---|---|
| The petsmart search as JSON. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Response fields
Field | Type | Description |
|---|---|---|
| string | — |
| string | The page we fetched. |
| number | What the call was charged, in credits, fractional (also |
| integer | — |
| object | — |
| string | — |
| integer | — |
| integer or null | — |
| integer or null | Send as |
| string or null | Poshmark only: send as |
| object[] | — |
GET /v1/structured/samsclub/search
Sam's Club search.
Sam's Club search results for a query, in the site's order: item id, title, brand (often null), price and regular price, rating, review count, stock and image. about 45 per page; send next_page back as page for more. Each id is what POST /v1/products takes for Sam's Club. Sponsored placements are left out. Sam's Club often refuses a request: a call can answer 502 TARGET_FAILED (not charged), so retry it, and a keyword that lands on a category page instead of results is a failure, not an empty list.
Cost: charged credits for the data it moves, like every endpoint. Also accepts POST with a JSON body.
Query parameters
Name | Type | Required | Description |
|---|---|---|---|
| string | yes | The search terms. |
| integer | — | Results page, 1–100. Default |
| string | — | Result order. Each site offers only the orders it supports. One of: |
Request
- cURL
- Python
- Node.js
curl -G "https://api.scrapelyserp.com/v1/structured/samsclub/search" \
-H "Authorization: Bearer $SCRAPELY_API_KEY" \
-d query=vitaminsimport os
import requests
HEADERS = {"Authorization": f"Bearer {os.environ['SCRAPELY_API_KEY']}"}
r = requests.get(
"https://api.scrapelyserp.com/v1/structured/samsclub/search",
headers=HEADERS,
params={"query": "vitamins"},
timeout=30,
)
r.raise_for_status()
data = r.json()const res = await fetch(`https://api.scrapelyserp.com/v1/structured/samsclub/search?${new URLSearchParams({ query: 'vitamins' })}`, {
headers: { Authorization: `Bearer ${process.env.SCRAPELY_API_KEY}` },
signal: AbortSignal.timeout(30_000),
});
if (!res.ok) throw new Error((await res.json()).error.code);
const data = await res.json();Response
{
"endpoint": "samsclub/search",
"url": "https://www.samsclub.com/s/vitamins",
"credits": 0.000028,
"duration_ms": 1480,
"data": {
"query": "vitamins",
"page": 1,
"total_results": 256,
"next_page": 2,
"results": [
{
"position": 1,
"id": "13899862238",
"title": "Member's Mark Advanced Women's 50+ Multivitamin Tablets, 275 ct.",
"url": "https://www.samsclub.com/ip/Member-s-Mark-Advanced-Women-s-50-Multivitamin-Tablets-275-ct/13899862238",
"brand": null,
"image": null,
"price": {
"amount": 13.34,
"currency": "USD"
},
"list_price": null,
"rating": 4.5,
"reviews_count": 859,
"in_stock": true,
"sponsored": false
}
]
}
}Responses
Status | Meaning |
|---|---|
| The sam's club search as JSON. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Response fields
Field | Type | Description |
|---|---|---|
| string | — |
| string | The page we fetched. |
| number | What the call was charged, in credits, fractional (also |
| integer | — |
| object | — |
| string | — |
| integer | — |
| integer or null | — |
| integer or null | Send as |
| string or null | Poshmark only: send as |
| object[] | — |
GET /v1/structured/walgreens/search
Walgreens search.
Walgreens search results for a query, in the site's order: product id, title, brand, price and regular price, rating, review count, stock and image. rating is in half stars (4.0, 4.5), not exact. Up to 72 per call, one page only (no page or sort); total_results counts all matches. in_stock is null when Walgreens does not say. Each id is what POST /v1/products takes for Walgreens. A keyword that lands on a service page instead of products (such as 'prescription') is a failure, not an empty list, and so is a block: a call can answer 502 TARGET_FAILED (not charged), so retry it. Prescription items are never returned.
Cost: charged credits for the data it moves, like every endpoint. Also accepts POST with a JSON body.
Query parameters
Name | Type | Required | Description |
|---|---|---|---|
| string | yes | The search terms. |
Request
- cURL
- Python
- Node.js
curl -G "https://api.scrapelyserp.com/v1/structured/walgreens/search" \
-H "Authorization: Bearer $SCRAPELY_API_KEY" \
-d query=shampooimport os
import requests
HEADERS = {"Authorization": f"Bearer {os.environ['SCRAPELY_API_KEY']}"}
r = requests.get(
"https://api.scrapelyserp.com/v1/structured/walgreens/search",
headers=HEADERS,
params={"query": "shampoo"},
timeout=30,
)
r.raise_for_status()
data = r.json()const res = await fetch(`https://api.scrapelyserp.com/v1/structured/walgreens/search?${new URLSearchParams({ query: 'shampoo' })}`, {
headers: { Authorization: `Bearer ${process.env.SCRAPELY_API_KEY}` },
signal: AbortSignal.timeout(30_000),
});
if (!res.ok) throw new Error((await res.json()).error.code);
const data = await res.json();Response
{
"endpoint": "walgreens/search",
"url": "https://www.walgreens.com/q/shampoo",
"credits": 0.000028,
"duration_ms": 1480,
"data": {
"query": "shampoo",
"page": 1,
"total_results": 526,
"next_page": null,
"results": [
{
"position": 1,
"id": "300452904",
"title": "Walgreens Shiny & Smooth Shampoo",
"url": "https://www.walgreens.com/store/c/walgreens-shiny-%26-smooth-shampoo/ID=300452904-product",
"brand": "Walgreens",
"image": null,
"price": {
"amount": 3.74,
"currency": "USD"
},
"list_price": {
"amount": 4.99,
"currency": "USD"
},
"rating": 4,
"reviews_count": 90,
"in_stock": true
}
]
}
}Responses
Status | Meaning |
|---|---|
| The walgreens search as JSON. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Response fields
Field | Type | Description |
|---|---|---|
| string | — |
| string | The page we fetched. |
| number | What the call was charged, in credits, fractional (also |
| integer | — |
| object | — |
| string | — |
| integer | — |
| integer or null | — |
| integer or null | Send as |
| string or null | Poshmark only: send as |
| object[] | — |
GET /v1/structured/cvs/search
CVS search.
CVS search results for a query, in the site's order: product id, title, brand, price (the lowest, with price_max when the product has variants at different prices) and regular price, rating, review count and image. 40 per page; send next_page back as page for more. sort is best_match or price_low. in_stock is always null: search does not report availability. Sponsored placements and prescription items are left out. Each id is what POST /v1/products takes for CVS. CVS often refuses a request: a call can answer 502 TARGET_FAILED (not charged), so retry it, and an answer that is not clearly a list of products or a count of zero is a failure, not an empty list.
Cost: charged credits for the data it moves, like every endpoint. Also accepts POST with a JSON body.
Query parameters
Name | Type | Required | Description |
|---|---|---|---|
| string | yes | The search terms. |
| integer | — | Results page, 1–100. Default |
| string | — | Result order. Each site offers only the orders it supports. One of: |
Request
- cURL
- Python
- Node.js
curl -G "https://api.scrapelyserp.com/v1/structured/cvs/search" \
-H "Authorization: Bearer $SCRAPELY_API_KEY" \
-d query=shampooimport os
import requests
HEADERS = {"Authorization": f"Bearer {os.environ['SCRAPELY_API_KEY']}"}
r = requests.get(
"https://api.scrapelyserp.com/v1/structured/cvs/search",
headers=HEADERS,
params={"query": "shampoo"},
timeout=30,
)
r.raise_for_status()
data = r.json()const res = await fetch(`https://api.scrapelyserp.com/v1/structured/cvs/search?${new URLSearchParams({ query: 'shampoo' })}`, {
headers: { Authorization: `Bearer ${process.env.SCRAPELY_API_KEY}` },
signal: AbortSignal.timeout(30_000),
});
if (!res.ok) throw new Error((await res.json()).error.code);
const data = await res.json();Response
{
"endpoint": "cvs/search",
"url": "https://www.cvs.com/search?searchTerm=shampoo",
"credits": 0.000028,
"duration_ms": 1480,
"data": {
"query": "shampoo",
"page": 1,
"total_results": 497,
"next_page": 2,
"results": [
{
"position": 1,
"id": "949346",
"title": "Carol's Daughter Lisa's Kitchen Scalp Detox Cleansing Nectar, 8 OZ",
"url": "https://www.cvs.com/shop/carol-s-daughter-lisa-s-kitchen-scalp-detox-cleansing-nectar-8-oz-prodid-949346",
"brand": "Carol's Daughter",
"image": null,
"price": {
"amount": 12.99,
"currency": "USD"
},
"list_price": null,
"rating": 4.74,
"reviews_count": 153,
"in_stock": null
}
]
}
}Responses
Status | Meaning |
|---|---|
| The cvs search as JSON. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Response fields
Field | Type | Description |
|---|---|---|
| string | — |
| string | The page we fetched. |
| number | What the call was charged, in credits, fractional (also |
| integer | — |
| object | — |
| string | — |
| integer | — |
| integer or null | — |
| integer or null | Send as |
| string or null | Poshmark only: send as |
| object[] | — |
GET /v1/structured/bjs/search
BJ's search.
BJ's search results for a query, in the site's order: item number, title, brand (null when BJ's gives none), price_range (low and high), rating, review count and image. 100 per page; send next_page back as page for more. sort is best_match, rating or newest. price and list_price are always null: BJ's data gives a price range, not the price at a given club, and the current club price is not available. in_stock is always null: search does not report availability. Each id is what POST /v1/products takes for BJ's. A keyword that matches nothing still returns BJ's nearest items, and an answer that is not clearly a list of products or a count of zero is a failure (502 TARGET_FAILED, not charged, retry it), not an empty list. Prescription items are never returned.
Cost: charged credits for the data it moves, like every endpoint. Also accepts POST with a JSON body.
Query parameters
Name | Type | Required | Description |
|---|---|---|---|
| string | yes | The search terms. |
| integer | — | Results page, 1–100. Default |
| string | — | Result order. Each site offers only the orders it supports. One of: |
Request
- cURL
- Python
- Node.js
curl -G "https://api.scrapelyserp.com/v1/structured/bjs/search" \
-H "Authorization: Bearer $SCRAPELY_API_KEY" \
-d query=coffeeimport os
import requests
HEADERS = {"Authorization": f"Bearer {os.environ['SCRAPELY_API_KEY']}"}
r = requests.get(
"https://api.scrapelyserp.com/v1/structured/bjs/search",
headers=HEADERS,
params={"query": "coffee"},
timeout=30,
)
r.raise_for_status()
data = r.json()const res = await fetch(`https://api.scrapelyserp.com/v1/structured/bjs/search?${new URLSearchParams({ query: 'coffee' })}`, {
headers: { Authorization: `Bearer ${process.env.SCRAPELY_API_KEY}` },
signal: AbortSignal.timeout(30_000),
});
if (!res.ok) throw new Error((await res.json()).error.code);
const data = await res.json();Response
{
"endpoint": "bjs/search",
"url": "https://www.bjs.com/search/coffee",
"credits": 0.000028,
"duration_ms": 1480,
"data": {
"query": "coffee",
"page": 1,
"total_results": 219,
"next_page": 2,
"results": [
{
"position": 1,
"id": "328157",
"title": "Maxwell House Medium Roast Original Ground Coffee, 43.1 oz.",
"url": "https://www.bjs.com/product/maxwell-house-medium-roast-original-ground-coffee-431-oz/3000000000005070289/",
"brand": null,
"image": "https://bjs.scene7.com/is/image/bjs/328157",
"price": null,
"list_price": null,
"price_range": {
"min": {
"amount": 15.29,
"currency": "USD"
},
"max": {
"amount": 22.99,
"currency": "USD"
}
},
"rating": 5,
"reviews_count": 1344,
"in_stock": null
}
]
}
}Responses
Status | Meaning |
|---|---|
| The bj's search as JSON. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Response fields
Field | Type | Description |
|---|---|---|
| string | — |
| string | The page we fetched. |
| number | What the call was charged, in credits, fractional (also |
| integer | — |
| object | — |
| string | — |
| integer | — |
| integer or null | — |
| integer or null | Send as |
| string or null | Poshmark only: send as |
| object[] | — |
GET /v1/structured/stockx/search
StockX search.
StockX search results for a query, in the site's order: slug, title, brand, lowest ask (as price), whether anything is on sale, and image. 40 per page; send next_page back as page for more. Each id is what POST /v1/products takes for StockX.
Cost: charged credits for the data it moves, like every endpoint. Also accepts POST with a JSON body.
Query parameters
Name | Type | Required | Description |
|---|---|---|---|
| string | yes | The search terms. |
| integer | — | Results page, 1–100. Default |
| string | — | Result order. Each site offers only the orders it supports. One of: |
Request
- cURL
- Python
- Node.js
curl -G "https://api.scrapelyserp.com/v1/structured/stockx/search" \
-H "Authorization: Bearer $SCRAPELY_API_KEY" \
--data-urlencode "query=dunk low"import os
import requests
HEADERS = {"Authorization": f"Bearer {os.environ['SCRAPELY_API_KEY']}"}
r = requests.get(
"https://api.scrapelyserp.com/v1/structured/stockx/search",
headers=HEADERS,
params={"query": "dunk low"},
timeout=30,
)
r.raise_for_status()
data = r.json()const res = await fetch(`https://api.scrapelyserp.com/v1/structured/stockx/search?${new URLSearchParams({ query: 'dunk low' })}`, {
headers: { Authorization: `Bearer ${process.env.SCRAPELY_API_KEY}` },
signal: AbortSignal.timeout(30_000),
});
if (!res.ok) throw new Error((await res.json()).error.code);
const data = await res.json();Response
{
"endpoint": "stockx/search",
"url": "https://stockx.com/search?s=dunk%20low",
"credits": 0.000028,
"duration_ms": 1480,
"data": {
"query": "dunk low",
"page": 1,
"total_results": 1000,
"next_page": 2,
"results": [
{
"position": 1,
"id": "nike-dunk-low-retro-white-black-2021",
"title": "Nike Dunk Low Retro White Black Panda",
"url": "https://stockx.com/nike-dunk-low-retro-white-black-2021",
"brand": "Nike",
"image": null,
"price": {
"amount": 59,
"currency": "USD"
},
"list_price": null,
"rating": null,
"reviews_count": null,
"in_stock": true,
"sponsored": false
}
]
}
}Responses
Status | Meaning |
|---|---|
| The stockx search as JSON. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Response fields
Field | Type | Description |
|---|---|---|
| string | — |
| string | The page we fetched. |
| number | What the call was charged, in credits, fractional (also |
| integer | — |
| object | — |
| string | — |
| integer | — |
| integer or null | — |
| integer or null | Send as |
| string or null | Poshmark only: send as |
| object[] | — |
GET /v1/structured/goat/search
GOAT search.
GOAT search results for a query, in the site's order: slug, title, brand, lowest price, retail price, stock and image. 24 per page; send next_page back as page for more. Each id is what POST /v1/products takes for GOAT.
Cost: charged credits for the data it moves, like every endpoint. Also accepts POST with a JSON body.
Query parameters
Name | Type | Required | Description |
|---|---|---|---|
| string | yes | The search terms. |
| integer | — | Results page, 1–100. Default |
| string | — | Result order. Each site offers only the orders it supports. One of: |
Request
- cURL
- Python
- Node.js
curl -G "https://api.scrapelyserp.com/v1/structured/goat/search" \
-H "Authorization: Bearer $SCRAPELY_API_KEY" \
--data-urlencode "query=dunk low"import os
import requests
HEADERS = {"Authorization": f"Bearer {os.environ['SCRAPELY_API_KEY']}"}
r = requests.get(
"https://api.scrapelyserp.com/v1/structured/goat/search",
headers=HEADERS,
params={"query": "dunk low"},
timeout=30,
)
r.raise_for_status()
data = r.json()const res = await fetch(`https://api.scrapelyserp.com/v1/structured/goat/search?${new URLSearchParams({ query: 'dunk low' })}`, {
headers: { Authorization: `Bearer ${process.env.SCRAPELY_API_KEY}` },
signal: AbortSignal.timeout(30_000),
});
if (!res.ok) throw new Error((await res.json()).error.code);
const data = await res.json();Response
{
"endpoint": "goat/search",
"url": "https://www.goat.com/search?query=dunk%20low",
"credits": 0.000028,
"duration_ms": 1480,
"data": {
"query": "dunk low",
"page": 1,
"total_results": 3239,
"next_page": 2,
"results": [
{
"position": 1,
"id": "dunk-low-black-white-dd1391-100",
"title": "Nike Dunk Low 'Black White'",
"url": "https://www.goat.com/sneakers/dunk-low-black-white-dd1391-100",
"brand": "Nike",
"image": null,
"price": {
"amount": 59,
"currency": "USD"
},
"list_price": {
"amount": 100,
"currency": "USD"
},
"rating": null,
"reviews_count": null,
"in_stock": true,
"sponsored": false
}
]
}
}Responses
Status | Meaning |
|---|---|
| The goat search as JSON. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Response fields
Field | Type | Description |
|---|---|---|
| string | — |
| string | The page we fetched. |
| number | What the call was charged, in credits, fractional (also |
| integer | — |
| object | — |
| string | — |
| integer | — |
| integer or null | — |
| integer or null | Send as |
| string or null | Poshmark only: send as |
| object[] | — |
GET /v1/structured/tcgplayer/search
TCGplayer search.
TCGplayer search results for a query, in the site's order: product id, title, market price (as price), whether any listing exists, and image. 50 per page; send next_page back as page for more. sort is best_match, best_selling, price_low, price_high (both on the market price), listing_low (cheapest listing first), newest or name; product_line narrows to one game (pokemon, yugioh, star-wars-unlimited). price is TCGplayer's market price, a figure from recent sales for the product, and null when it has none; list_price, rating and reviews_count are always null. in_stock is true when at least one listing exists. Each id is what POST /v1/products takes for TCGplayer. An answer that is not clearly a list of products or a count of zero is a failure (502 TARGET_FAILED, not charged, retry it), not an empty list.
Cost: charged credits for the data it moves, like every endpoint. Also accepts POST with a JSON body.
Query parameters
Name | Type | Required | Description |
|---|---|---|---|
| string | yes | The search terms. |
| integer | — | Results page, 1–100. Default |
| string | — | Result order. Each site offers only the orders it supports. One of: |
| string | — | Only this game or product line, by its lower-case name ( |
Request
- cURL
- Python
- Node.js
curl -G "https://api.scrapelyserp.com/v1/structured/tcgplayer/search" \
-H "Authorization: Bearer $SCRAPELY_API_KEY" \
-d query=charizardimport os
import requests
HEADERS = {"Authorization": f"Bearer {os.environ['SCRAPELY_API_KEY']}"}
r = requests.get(
"https://api.scrapelyserp.com/v1/structured/tcgplayer/search",
headers=HEADERS,
params={"query": "charizard"},
timeout=30,
)
r.raise_for_status()
data = r.json()const res = await fetch(`https://api.scrapelyserp.com/v1/structured/tcgplayer/search?${new URLSearchParams({ query: 'charizard' })}`, {
headers: { Authorization: `Bearer ${process.env.SCRAPELY_API_KEY}` },
signal: AbortSignal.timeout(30_000),
});
if (!res.ok) throw new Error((await res.json()).error.code);
const data = await res.json();Response
{
"endpoint": "tcgplayer/search",
"url": "https://www.tcgplayer.com/search/all/product?q=charizard",
"credits": 0.000028,
"duration_ms": 1480,
"data": {
"query": "charizard",
"page": 1,
"total_results": 382,
"next_page": 2,
"results": [
{
"position": 1,
"id": "662182",
"title": "Mega Charizard X ex - 013/094",
"url": "https://www.tcgplayer.com/product/662182/pokemon-me02-phantasmal-flames-mega-charizard-x-ex-013-094",
"brand": null,
"image": "https://product-images.tcgplayer.com/fit-in/437x608/662182.jpg",
"price": {
"amount": 3.55,
"currency": "USD"
},
"list_price": null,
"rating": null,
"reviews_count": null,
"in_stock": true
}
]
}
}Responses
Status | Meaning |
|---|---|
| The tcgplayer search as JSON. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Response fields
Field | Type | Description |
|---|---|---|
| string | — |
| string | The page we fetched. |
| number | What the call was charged, in credits, fractional (also |
| integer | — |
| object | — |
| string | — |
| integer | — |
| integer or null | — |
| integer or null | Send as |
| string or null | Poshmark only: send as |
| object[] | — |
GET /v1/structured/staples/search
Staples search.
Staples search results for a query, in the site's order: item id, title, brand, price (for one unit), link and image. 100 per page; send next_page back as page for more, up to page 100 (10,000 results). sort is best_match, price_low or price_high. list_price, rating, reviews_count and in_stock are always null: search does not carry them, ask POST /v1/products. A set of options (an id starting SS) has price null. Each id is what POST /v1/products takes for Staples. A keyword that matches nothing still returns Staples' nearest item (one result), and an answer that is not clearly a list of products or a count of zero is a failure (502 TARGET_FAILED, not charged, retry it), not an empty list.
Cost: charged credits for the data it moves, like every endpoint. Also accepts POST with a JSON body.
Query parameters
Name | Type | Required | Description |
|---|---|---|---|
| string | yes | The search terms. |
| integer | — | Results page, 1–100. Default |
| string | — | Result order. Each site offers only the orders it supports. One of: |
Request
- cURL
- Python
- Node.js
curl -G "https://api.scrapelyserp.com/v1/structured/staples/search" \
-H "Authorization: Bearer $SCRAPELY_API_KEY" \
--data-urlencode "query=copy paper"import os
import requests
HEADERS = {"Authorization": f"Bearer {os.environ['SCRAPELY_API_KEY']}"}
r = requests.get(
"https://api.scrapelyserp.com/v1/structured/staples/search",
headers=HEADERS,
params={"query": "copy paper"},
timeout=30,
)
r.raise_for_status()
data = r.json()const res = await fetch(`https://api.scrapelyserp.com/v1/structured/staples/search?${new URLSearchParams({ query: 'copy paper' })}`, {
headers: { Authorization: `Bearer ${process.env.SCRAPELY_API_KEY}` },
signal: AbortSignal.timeout(30_000),
});
if (!res.ok) throw new Error((await res.json()).error.code);
const data = await res.json();Response
{
"endpoint": "staples/search",
"url": "https://www.staples.com/copy%20paper/directory_copy%20paper",
"credits": 0.000028,
"duration_ms": 1480,
"data": {
"query": "copy paper",
"page": 1,
"total_results": 11201,
"next_page": 2,
"results": [
{
"position": 1,
"id": "122374",
"title": "Hammermill Copy Plus 8.5\" x 11\" Copy Paper, 20 lbs., 92 Brightness, 5000 Sheets/Carton (105007)",
"url": "https://www.staples.com/product_122374",
"brand": "Hammermill",
"image": "https://www.staples-3p.com/s7/is/image/Staples/0A9DEFAA-5A39-42C6-938F42EAB5C0ACCF",
"price": {
"amount": 49.99,
"currency": "USD"
},
"list_price": null,
"rating": null,
"reviews_count": null,
"in_stock": null
}
]
}
}Responses
Status | Meaning |
|---|---|
| The staples search as JSON. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Response fields
Field | Type | Description |
|---|---|---|
| string | — |
| string | The page we fetched. |
| number | What the call was charged, in credits, fractional (also |
| integer | — |
| object | — |
| string | — |
| integer | — |
| integer or null | — |
| integer or null | Send as |
| string or null | Poshmark only: send as |
| object[] | — |
GET /v1/structured/kohls/search
Kohl's search.
Kohl's search results for a query, a category, or both, in the site's order: product id, title, price, regular price, rating, review count, link and image. 120 per page; send next_page back as page for more. sort is best_match, newest, price_low, price_high or rating. category is a Kohl's category value (the CN= part of a Kohl's catalog link, Gender:Mens+Category:Bottoms+Department:Clothing); with a category the query is optional and narrows within it. price is the regular price, or the sale price when marked down; it is never the 20% coupon price Kohl's pages lead with. A product with options at different prices has price null and a price_range (low and high); list_price is the regular price of a product on sale at one price. Sponsored placements are left out. brand is always null and in_stock is always null: Kohl's search carries neither. Each id is what POST /v1/products takes for Kohl's. A keyword that matches nothing still returns Kohl's closest items (33 for a nonsense word), and a category Kohl's does not recognize, a page past the last one, or an answer that is not clearly a list of products or a count of zero is a failure (502 TARGET_FAILED, not charged, retry it), not an empty list. A request with neither query nor category is refused.
Cost: charged credits for the data it moves, like every endpoint. Also accepts POST with a JSON body.
Query parameters
Name | Type | Required | Description |
|---|---|---|---|
| string | — | The search terms. Required unless |
| string | — | A Kohl's category value (the |
| integer | — | Results page, 1–100. Default |
| string | — | Result order. Each site offers only the orders it supports. One of: |
Request
- cURL
- Python
- Node.js
curl -G "https://api.scrapelyserp.com/v1/structured/kohls/search" \
-H "Authorization: Bearer $SCRAPELY_API_KEY" \
--data-urlencode "query=mens jeans"import os
import requests
HEADERS = {"Authorization": f"Bearer {os.environ['SCRAPELY_API_KEY']}"}
r = requests.get(
"https://api.scrapelyserp.com/v1/structured/kohls/search",
headers=HEADERS,
params={"query": "mens jeans"},
timeout=30,
)
r.raise_for_status()
data = r.json()const res = await fetch(`https://api.scrapelyserp.com/v1/structured/kohls/search?${new URLSearchParams({ query: 'mens jeans' })}`, {
headers: { Authorization: `Bearer ${process.env.SCRAPELY_API_KEY}` },
signal: AbortSignal.timeout(30_000),
});
if (!res.ok) throw new Error((await res.json()).error.code);
const data = await res.json();Response
{
"endpoint": "kohls/search",
"url": "https://www.kohls.com/search.jsp?search=mens%20jeans",
"credits": 0.000028,
"duration_ms": 1480,
"data": {
"query": "mens jeans",
"page": 1,
"total_results": 2198,
"next_page": 2,
"results": [
{
"position": 1,
"id": "7431597",
"title": "Sonoma Goods For Life Hygro Performance Bath Towel, Bath Sheet, Hand Towel or Washcloth",
"url": "https://www.kohls.com/product/prd-7431597/sonoma-goods-for-life-hygro-performance-bath-towel-bath-sheet-hand-towel-or-washcloth.jsp",
"brand": null,
"image": "https://media.kohlsimg.com/is/image/kohls/7431597_Blush?wid=180&hei=180&op_sharpen=1",
"price": null,
"price_range": {
"min": {
"amount": 7.99,
"currency": "USD"
},
"max": {
"amount": 49.99,
"currency": "USD"
}
},
"list_price": null,
"rating": 4.4,
"reviews_count": 14485,
"in_stock": null
},
{
"position": 2,
"id": "8363284",
"title": "Men's Vintage America Straight Fit Denim Jeans",
"url": "https://www.kohls.com/product/prd-8363284/mens-vintage-america-straight-fit-denim-jeans.jsp",
"brand": null,
"image": "https://media.kohlsimg.com/is/image/kohls/8363284_Liberty_Wash?wid=180&hei=180&op_sharpen=1",
"price": {
"amount": 26.4,
"currency": "USD"
},
"price_range": null,
"list_price": {
"amount": 44,
"currency": "USD"
},
"rating": 5,
"reviews_count": 5,
"in_stock": null
}
]
}
}Responses
Status | Meaning |
|---|---|
| The kohl's search as JSON. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Response fields
Field | Type | Description |
|---|---|---|
| string | — |
| string | The page we fetched. |
| number | What the call was charged, in credits, fractional (also |
| integer | — |
| object | — |
| string | — |
| integer | — |
| integer or null | — |
| integer or null | Send as |
| string or null | Poshmark only: send as |
| object[] | — |
GET /v1/structured/officedepot/search
Office Depot search.
Office Depot search results for a query, in the site's order: sku, title, brand, price (for one unit), rating, review count, link and image. 24 per page; send next_page back as page for more. sort is best_match, price_low, price_high or rating (Office Depot's Customer Rating order, which favours products with many good reviews over a high average). list_price and in_stock are always null: Office Depot's search results show a wrong list price and a wrong stock status, so ask POST /v1/products for the skus you want. rating and reviews_count are null with no reviews. Sponsored placements are left out. Each id is what POST /v1/products takes for Office Depot. A keyword that matches nothing still returns 200 of Office Depot's products (24 a page, total_results exactly 200) and nothing in the answer marks it: a real search reports its true total (84, 487…), so a total_results of 200 whose titles do not contain your words means no match. An empty list means Office Depot says there are no matches; any other unclear answer is a failure (502 TARGET_FAILED, not charged, retry it).
Cost: charged credits for the data it moves, like every endpoint. Also accepts POST with a JSON body.
Query parameters
Name | Type | Required | Description |
|---|---|---|---|
| string | yes | The search terms. |
| integer | — | Results page, 1–100. Default |
| string | — | Result order. Each site offers only the orders it supports. One of: |
Request
- cURL
- Python
- Node.js
curl -G "https://api.scrapelyserp.com/v1/structured/officedepot/search" \
-H "Authorization: Bearer $SCRAPELY_API_KEY" \
--data-urlencode "query=printer paper"import os
import requests
HEADERS = {"Authorization": f"Bearer {os.environ['SCRAPELY_API_KEY']}"}
r = requests.get(
"https://api.scrapelyserp.com/v1/structured/officedepot/search",
headers=HEADERS,
params={"query": "printer paper"},
timeout=30,
)
r.raise_for_status()
data = r.json()const res = await fetch(`https://api.scrapelyserp.com/v1/structured/officedepot/search?${new URLSearchParams({ query: 'printer paper' })}`, {
headers: { Authorization: `Bearer ${process.env.SCRAPELY_API_KEY}` },
signal: AbortSignal.timeout(30_000),
});
if (!res.ok) throw new Error((await res.json()).error.code);
const data = await res.json();Response
{
"endpoint": "officedepot/search",
"url": "https://www.officedepot.com/a/search/?q=printer%20paper",
"credits": 0.000028,
"duration_ms": 1480,
"data": {
"query": "printer paper",
"page": 1,
"total_results": 487,
"next_page": 2,
"results": [
{
"position": 1,
"id": "301742",
"title": "JAM Paper® Color Multi-Use Printer & Copy Paper, Brown Kraft, Letter (8.5\" x 11\"), 50 Sheets Per Pack, 28 Lb",
"url": "https://www.officedepot.com/a/products/301742/JAM-Paper-Color-Multi-Use-Printer/",
"brand": "JAM Paper",
"image": "https://media.officedepot.com/images/t_large%2Cf_auto/products/301742/1.jpg",
"price": {
"amount": 29.99,
"currency": "USD"
},
"list_price": null,
"rating": 4.67,
"reviews_count": 3,
"in_stock": null
},
{
"position": 2,
"id": "196517",
"title": "Boise® X-9® Multi-Use Printer & Copy Paper, 10 Reams, White, Letter (8.5\" x 11\"), 5000 Sheets Per Case, 20 Lb, 92 Brightness",
"url": "https://www.officedepot.com/a/products/196517/Boise-X-9-Multi-Use-Printer/",
"brand": "BOISE",
"image": "https://media.officedepot.com/images/t_large%2Cf_auto/products/196517/1.jpg",
"price": {
"amount": 48.99,
"currency": "USD"
},
"list_price": null,
"rating": 4.68,
"reviews_count": 20060,
"in_stock": null
}
]
}
}Responses
Status | Meaning |
|---|---|
| The office depot search as JSON. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Response fields
Field | Type | Description |
|---|---|---|
| string | — |
| string | The page we fetched. |
| number | What the call was charged, in credits, fractional (also |
| integer | — |
| object | — |
| string | — |
| integer | — |
| integer or null | — |
| integer or null | Send as |
| string or null | Poshmark only: send as |
| object[] | — |
GET /v1/structured/ikea/search
IKEA search.
IKEA search results for a query or a category, in the site's order: item number, title, price, regular price, rating, review count, link and image. 100 per page; send next_page back as page for more. sort is best_match, price_low, price_high, newest or rating. category is an IKEA category key (fu003 is sofas & sectionals); with a category the query is optional and narrows within it. price is IKEA's public price. A product that shows an IKEA Family (member) price has that member price in POST /v1/products, never here; list_price is the earlier or regular price of a marked-down product. brand is always null and in_stock is always null: search carries neither (ask POST /v1/products for online availability). Each id is what POST /v1/products takes for IKEA. A keyword that matches nothing returns zero results (200, total_results 0). A category key IKEA does not know, a page past the last one, or an answer that is not clearly a list of products or a count of zero is a failure (502 TARGET_FAILED, not charged, retry it), not an empty list. A request with neither query nor category is refused.
Cost: charged credits for the data it moves, like every endpoint. Also accepts POST with a JSON body.
Query parameters
Name | Type | Required | Description |
|---|---|---|---|
| string | — | The search terms (at most 150 characters). Required unless |
| string | — | An IKEA category key, e.g. |
| integer | — | Results page, 1–100. Default |
| string | — | Result order. Each site offers only the orders it supports. One of: |
Request
- cURL
- Python
- Node.js
curl -G "https://api.scrapelyserp.com/v1/structured/ikea/search" \
-H "Authorization: Bearer $SCRAPELY_API_KEY" \
-d query=deskimport os
import requests
HEADERS = {"Authorization": f"Bearer {os.environ['SCRAPELY_API_KEY']}"}
r = requests.get(
"https://api.scrapelyserp.com/v1/structured/ikea/search",
headers=HEADERS,
params={"query": "desk"},
timeout=30,
)
r.raise_for_status()
data = r.json()const res = await fetch(`https://api.scrapelyserp.com/v1/structured/ikea/search?${new URLSearchParams({ query: 'desk' })}`, {
headers: { Authorization: `Bearer ${process.env.SCRAPELY_API_KEY}` },
signal: AbortSignal.timeout(30_000),
});
if (!res.ok) throw new Error((await res.json()).error.code);
const data = await res.json();Response
{
"endpoint": "ikea/search",
"url": "https://www.ikea.com/us/en/search/?q=desk",
"credits": 0.000028,
"duration_ms": 1480,
"data": {
"query": "desk",
"page": 1,
"total_results": 187,
"next_page": 2,
"results": [
{
"position": 1,
"id": "10514155",
"title": "IDANÄS Desk, white, 59 7/8x27 1/2 \"",
"url": "https://www.ikea.com/us/en/p/idanaes-desk-white-10514155/",
"brand": null,
"image": "https://www.ikea.com/us/en/images/products/idanaes-desk-white__1159443_pe888489_s5.jpg",
"price": {
"amount": 449,
"currency": "USD"
},
"list_price": {
"amount": 599,
"currency": "USD"
},
"rating": 4.3,
"reviews_count": 31,
"in_stock": null
},
{
"position": 2,
"id": "60609679",
"title": "GULLABERG L-shaped desk, drop file storage/white, 61x61x29 1/2 \"",
"url": "https://www.ikea.com/us/en/p/gullaberg-l-shaped-desk-drop-file-storage-white-60609679/",
"brand": null,
"image": "https://www.ikea.com/us/en/images/products/gullaberg-l-shaped-desk-drop-file-storage-white__1536835_pe1016995_s5.jpg",
"price": {
"amount": 399.99,
"currency": "USD"
},
"list_price": null,
"rating": null,
"reviews_count": null,
"in_stock": null
}
]
}
}Responses
Status | Meaning |
|---|---|
| The ikea search as JSON. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Response fields
Field | Type | Description |
|---|---|---|
| string | — |
| string | The page we fetched. |
| number | What the call was charged, in credits, fractional (also |
| integer | — |
| object | — |
| string | — |
| integer | — |
| integer or null | — |
| integer or null | Send as |
| string or null | Poshmark only: send as |
| object[] | — |
GET /v1/structured/poshmark/search
Poshmark search.
Poshmark listings for a query, in the site's order: listing id, title, brand, price, original price, size, condition, stock per size, seller, department, category, likes, link and image. Up to 100 per page for available listings (48 for status=sold); page on with cursor: send next_cursor back as cursor. sort is best_match, newest (just listed), price_low or price_high; condition=new_with_tags and min_price / max_price (dollars) narrow the listings. status is available (the default) or sold; sold listings cost about 5 times as much per listing and take sort and condition but not the price filters. About 5,000 listings are reachable for one query, however many total_results says (it stops at 10,000). price is the seller's listing price, never including fees or shipping; list_price is the original price the seller gave, null when none. rating and reviews_count are always null: listings have none. in_stock is true for an available listing and false for a sold one; poshmark.sizes has the quantity per size. poshmark.condition is new_with_tags or Poshmark's own code, and null when the seller set none. Each id is what POST /v1/products takes for Poshmark. A keyword that matches nothing still returns loose matches (99 for a nonsense word), and an answer that is not clearly a list of listings or a total of zero is a failure (502 TARGET_FAILED, not charged, retry it), not an empty list.
Cost: charged credits for the data it moves, like every endpoint. Also accepts POST with a JSON body.
Query parameters
Name | Type | Required | Description |
|---|---|---|---|
| string | yes | The search terms. |
| string | — | The |
| string | — | Result order. Each site offers only the orders it supports. One of: |
| string | — | Listings for sale, or sold ones. Sold costs about 5 times as much per listing and returns 48 per page. One of: |
| string | — | Only listings new with tags. One of: |
| string | — | Lowest listing price, in dollars (a number). Not with |
| string | — | Highest listing price, in dollars (a number). Not with |
Request
- cURL
- Python
- Node.js
curl -G "https://api.scrapelyserp.com/v1/structured/poshmark/search" \
-H "Authorization: Bearer $SCRAPELY_API_KEY" \
--data-urlencode "query=levis jeans"import os
import requests
HEADERS = {"Authorization": f"Bearer {os.environ['SCRAPELY_API_KEY']}"}
r = requests.get(
"https://api.scrapelyserp.com/v1/structured/poshmark/search",
headers=HEADERS,
params={"query": "levis jeans"},
timeout=30,
)
r.raise_for_status()
data = r.json()const res = await fetch(`https://api.scrapelyserp.com/v1/structured/poshmark/search?${new URLSearchParams({ query: 'levis jeans' })}`, {
headers: { Authorization: `Bearer ${process.env.SCRAPELY_API_KEY}` },
signal: AbortSignal.timeout(30_000),
});
if (!res.ok) throw new Error((await res.json()).error.code);
const data = await res.json();Response
{
"endpoint": "poshmark/search",
"url": "https://poshmark.com/search?query=levis%20jeans",
"credits": 0.000028,
"duration_ms": 1480,
"data": {
"query": "levis jeans",
"page": 1,
"total_results": 10000,
"next_page": null,
"next_cursor": "ENC_eyJtYXhfaWRzIjpbMTAxLDEsMV0sIm1heF9pZF9zY2hlbWUiOiJvcmciLCJwYWdlX251bSI6MiwicGFnZV9ncm91cF9pZCI6IjZhZWNiODc5OGY5ZDNlOGQ3MDdjMmQ5NyJ9",
"results": [
{
"position": 1,
"id": "6aecb847f09fc893a4990038",
"title": "NWT signature Levi strauss Gold totally shaping pull on jeans",
"url": "https://poshmark.com/listing/6aecb847f09fc893a4990038",
"brand": "signature levi strauss gold",
"image": "https://di2ponv0v5otw.cloudfront.net/posts/2026/11/05/6aecb847f09fc893a4990038/m_6aecb84cc6a0b3f268778f5a.jpeg",
"price": {
"amount": 15,
"currency": "USD"
},
"list_price": null,
"rating": null,
"reviews_count": null,
"in_stock": true,
"poshmark": {
"listingStatus": "available",
"size": "32",
"condition": "new_with_tags",
"sizes": [
{
"size": "32",
"available": 1,
"sold": 0
}
],
"seller": "deedeshay0619",
"department": "Women",
"category": "Jeans",
"likes": 0
}
}
]
}
}Responses
Status | Meaning |
|---|---|
| The poshmark search as JSON. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Response fields
Field | Type | Description |
|---|---|---|
| string | — |
| string | The page we fetched. |
| number | What the call was charged, in credits, fractional (also |
| integer | — |
| object | — |
| string | — |
| integer | — |
| integer or null | — |
| integer or null | Send as |
| string or null | Poshmark only: send as |
| object[] | — |
GET /v1/structured/gamestop/search
GameStop search.
GameStop search results for a query, in the site's order: product id, title, brand (null when GameStop gives none), current price, link and image. 200 per page; send next_page back as page for more. sort is best_match, popular, price_low, price_high or newest. New, pre-owned, digital and refurbished copies are separate products with separate ids and prices; a row shows one of them (the best match), and POST /v1/products lists the others as gamestop.siblings. A pre-owned price can be higher than the new one: it is what GameStop's data says. list_price, rating, reviews_count and in_stock are always null: GameStop's data has no list price and no rating per product, and stock is not reported. Each id is what POST /v1/products takes for GameStop. A keyword that matches no word returns an empty list (total_results 0), not GameStop's fuzzy near-matches, and an answer that is not clearly a list of products or a count of zero is a failure (502 TARGET_FAILED, not charged, retry it), not an empty list.
Cost: charged credits for the data it moves, like every endpoint. Also accepts POST with a JSON body.
Query parameters
Name | Type | Required | Description |
|---|---|---|---|
| string | yes | The search terms. |
| integer | — | Results page, 1–100. Default |
| string | — | Result order. Each site offers only the orders it supports. One of: |
Request
- cURL
- Python
- Node.js
curl -G "https://api.scrapelyserp.com/v1/structured/gamestop/search" \
-H "Authorization: Bearer $SCRAPELY_API_KEY" \
--data-urlencode "query=mario kart"import os
import requests
HEADERS = {"Authorization": f"Bearer {os.environ['SCRAPELY_API_KEY']}"}
r = requests.get(
"https://api.scrapelyserp.com/v1/structured/gamestop/search",
headers=HEADERS,
params={"query": "mario kart"},
timeout=30,
)
r.raise_for_status()
data = r.json()const res = await fetch(`https://api.scrapelyserp.com/v1/structured/gamestop/search?${new URLSearchParams({ query: 'mario kart' })}`, {
headers: { Authorization: `Bearer ${process.env.SCRAPELY_API_KEY}` },
signal: AbortSignal.timeout(30_000),
});
if (!res.ok) throw new Error((await res.json()).error.code);
const data = await res.json();Response
{
"endpoint": "gamestop/search",
"url": "https://www.gamestop.com/search/?q=mario%20kart",
"credits": 0.000028,
"duration_ms": 1480,
"data": {
"query": "mario",
"page": 1,
"total_results": 512,
"next_page": 2,
"results": [
{
"position": 1,
"id": "393069",
"title": "Hallmark Super Mario PVC Figural Mini Ornament Set",
"url": "https://www.gamestop.com/home/bedroom/decor/products/hallmark-super-mario-pvc-figural-mini-ornament-set/393069.html",
"brand": "Hallmark",
"image": "https://media.gamestop.com/i/gamestop/20004908/Hallmark-Super-Mario-PVC-Figural-Mini-Ornament-Set?$thumb$",
"price": {
"amount": 9.97,
"currency": "USD"
},
"list_price": null,
"rating": null,
"reviews_count": null,
"in_stock": null
}
]
}
}Responses
Status | Meaning |
|---|---|
| The gamestop search as JSON. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Response fields
Field | Type | Description |
|---|---|---|
| string | — |
| string | The page we fetched. |
| number | What the call was charged, in credits, fractional (also |
| integer | — |
| object | — |
| string | — |
| integer | — |
| integer or null | — |
| integer or null | Send as |
| string or null | Poshmark only: send as |
| object[] | — |
GET /v1/structured/dicks/search
Dick's search.
Dick's Sporting Goods search results for a query, in the site's order: sku, title, brand, price, regular price, rating, review count and link. 144 per page; send next_page back as page for more. sort is best_match, price_low or price_high; brand narrows to one brand. Each result is a style shown in one colour, and its id is the sku of that colour and one size: it goes straight into POST /v1/products for Dick's, which returns that sku's price and stock. price is the sku's current price (a sale ends on a date: the price in force now), and list_price the regular price when it is marked down. in_stock is always null: search carries no stock. image is always null. The price orders follow the highest price Dick's has on file for a product, which can be a later non-sale price, so current prices are close to ordered, not exact. A keyword that matches nothing still returns Dick's closest items (2 for a nonsense word we tried) rather than an empty list; a brand that matches nothing is an empty list. A page past the last, and an answer that is not clearly a list of products or a count of zero, is a failure (502 TARGET_FAILED, not charged, retry it), not an empty list.
Cost: charged credits for the data it moves, like every endpoint. Also accepts POST with a JSON body.
Query parameters
Name | Type | Required | Description |
|---|---|---|---|
| string | yes | The search terms. |
| integer | — | Results page, 1–100. Default |
| string | — | Result order. Each site offers only the orders it supports. One of: |
| string | — | Only this brand, spelled as Dick's does, capital letters included ( |
Request
- cURL
- Python
- Node.js
curl -G "https://api.scrapelyserp.com/v1/structured/dicks/search" \
-H "Authorization: Bearer $SCRAPELY_API_KEY" \
--data-urlencode "query=running shoes"import os
import requests
HEADERS = {"Authorization": f"Bearer {os.environ['SCRAPELY_API_KEY']}"}
r = requests.get(
"https://api.scrapelyserp.com/v1/structured/dicks/search",
headers=HEADERS,
params={"query": "running shoes"},
timeout=30,
)
r.raise_for_status()
data = r.json()const res = await fetch(`https://api.scrapelyserp.com/v1/structured/dicks/search?${new URLSearchParams({ query: 'running shoes' })}`, {
headers: { Authorization: `Bearer ${process.env.SCRAPELY_API_KEY}` },
signal: AbortSignal.timeout(30_000),
});
if (!res.ok) throw new Error((await res.json()).error.code);
const data = await res.json();Response
{
"endpoint": "dicks/search",
"url": "https://www.dickssportinggoods.com/search/SearchDisplay?searchTerm=running%20shoes",
"credits": 0.000028,
"duration_ms": 1480,
"data": {
"query": "running shoes",
"page": 1,
"total_results": 719,
"next_page": 2,
"results": [
{
"position": 1,
"id": "28503063",
"title": "Nike Men's Alphafly 4 Road Racing Shoes",
"url": "https://www.dickssportinggoods.com/p/nike-mens-alphafly-4-road-racing-shoes-26nikmrunnnklphflyffe/26nikmrunnnklphflyffe",
"brand": "Nike",
"image": null,
"price": {
"amount": 279.99,
"currency": "USD"
},
"list_price": null,
"rating": null,
"reviews_count": null,
"in_stock": null
},
{
"position": 2,
"id": "26285440",
"title": "HOKA Women's Clifton 10 Running Shoes",
"url": "https://www.dickssportinggoods.com/p/hoka-womens-clifton-10-running-shoes-25fhqwclftn10blckftw/25fhqwclftn10blckftw",
"brand": "Hoka",
"image": null,
"price": {
"amount": 124.97,
"currency": "USD"
},
"list_price": {
"amount": 154.99,
"currency": "USD"
},
"rating": 4.73,
"reviews_count": 1970,
"in_stock": null
}
]
}
}Responses
Status | Meaning |
|---|---|
| The dick's search as JSON. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Response fields
Field | Type | Description |
|---|---|---|
| string | — |
| string | The page we fetched. |
| number | What the call was charged, in credits, fractional (also |
| integer | — |
| object | — |
| string | — |
| integer | — |
| integer or null | — |
| integer or null | Send as |
| string or null | Poshmark only: send as |
| object[] | — |
GET /v1/structured/grailed/search
Grailed search.
Grailed listings for a query, in the site's order: listing id, title, designers (as brand), asking price, original price, size, condition, department, category, seller and seller rating, likes, listed date, link and image; for a sold listing also the sold price, sold date and shipping paid. Up to 100 per page; send next_page back as page for more. sort is best_match, newest, price_low or price_high; status is available (the default) or sold; department, condition, min_price / max_price (whole dollars, on the asking price), size and designer narrow the listings. Only the first 1,000 listings of a query can be reached (page 1 to 10), however large total_results says: narrow the query with the filters to see more. price is the asking price, on a sold listing too: what a sold listing sold for is grailed.soldPrice (it can be lower, when an offer was accepted), with grailed.soldAt. list_price is the first price in the listing's price-drop history, null when the price never dropped. rating and reviews_count are always null: listings have none (the seller's own rating is grailed.sellerRating). in_stock is true for an available listing and false for a sold one. Each id is what POST /v1/products takes for Grailed. A keyword that matches nothing is an empty list (total_results 0), and an answer that is not clearly a list of listings or a total of zero is a failure (502 TARGET_FAILED, not charged, retry it), not an empty list.
Cost: charged credits for the data it moves, like every endpoint. Also accepts POST with a JSON body.
Query parameters
Name | Type | Required | Description |
|---|---|---|---|
| string | yes | The search terms. |
| integer | — | Results page, 1–10 (100 listings each: the first 1,000 of a query). Default |
| string | — | Result order: |
| string | — | Listings for sale, or sold ones (with their sold price and date). One of: |
| string | — | Only this department. One of: |
| string | — | Only listings in this condition. One of: |
| string | — | Lowest asking price, in whole dollars. |
| string | — | Highest asking price, in whole dollars. |
| string | — | Only this size, as Grailed writes it ( |
| string | — | Only listings by this designer ( |
Request
- cURL
- Python
- Node.js
curl -G "https://api.scrapelyserp.com/v1/structured/grailed/search" \
-H "Authorization: Bearer $SCRAPELY_API_KEY" \
--data-urlencode "query=air jordan"import os
import requests
HEADERS = {"Authorization": f"Bearer {os.environ['SCRAPELY_API_KEY']}"}
r = requests.get(
"https://api.scrapelyserp.com/v1/structured/grailed/search",
headers=HEADERS,
params={"query": "air jordan"},
timeout=30,
)
r.raise_for_status()
data = r.json()const res = await fetch(`https://api.scrapelyserp.com/v1/structured/grailed/search?${new URLSearchParams({ query: 'air jordan' })}`, {
headers: { Authorization: `Bearer ${process.env.SCRAPELY_API_KEY}` },
signal: AbortSignal.timeout(30_000),
});
if (!res.ok) throw new Error((await res.json()).error.code);
const data = await res.json();Response
{
"endpoint": "grailed/search",
"url": "https://www.grailed.com/shop?query=air%20jordan",
"credits": 0.000028,
"duration_ms": 1480,
"data": {
"query": "air jordan",
"page": 1,
"total_results": 44619,
"next_page": 2,
"results": [
{
"position": 1,
"id": "111350058",
"title": "Fragment Design x Union LA x Air Jordan 1 Retro High OG",
"url": "https://www.grailed.com/listings/111350058",
"brand": "Fragment Design × Jordan Brand",
"image": "https://media-assets.grailed.com/prd/listing/temp/ee3b918ddca746e8b638acc18d2123aa",
"price": {
"amount": 170,
"currency": "USD"
},
"list_price": null,
"rating": null,
"reviews_count": null,
"in_stock": true,
"grailed": {
"status": "available",
"size": "11",
"condition": "gently_used",
"category": "footwear",
"department": "menswear",
"seller": "616Selects",
"sellerRating": 5,
"sellerRatingCount": 13,
"likes": 8,
"listedAt": "2026-11-04T21:13:58.022Z",
"soldPrice": null,
"soldAt": null,
"soldShippingPrice": null
}
}
]
}
}Responses
Status | Meaning |
|---|---|
| The grailed search as JSON. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Response fields
Field | Type | Description |
|---|---|---|
| string | — |
| string | The page we fetched. |
| number | What the call was charged, in credits, fractional (also |
| integer | — |
| object | — |
| string | — |
| integer | — |
| integer or null | — |
| integer or null | Send as |
| string or null | Poshmark only: send as |
| object[] | — |
GET /v1/structured/macys/search
Macy's search.
Macy's search results for a keyword or a category, in the site's order: product id, title, brand, price, regular price, rating, review count, stock flag, link and image. 60 to 64 per page; send next_page back as page for more. sort is best_match, best_selling, newest, price_low, price_high or rating. category is a Macy's category id (the number after id= in a category link); send query or category, not both. price is the regular price, or the sale price when marked down; it is never a coupon price. A product with options at different prices has price null and a price_range (low and high); list_price is the regular price of a product on sale at one price. in_stock is Macy's own flag on the product, null for a collection of products. Each id is what POST /v1/products takes for Macy's. A keyword that matches nothing still returns Macy's closest items (about 60 on page 1, 97 in total for a nonsense word), and a category Macy's does not recognize, a page past the last one, or an answer that is not clearly a list of products or a count of zero is a failure (502 TARGET_FAILED, not charged, retry it), not an empty list. A request with neither query nor category, or with both, is refused.
Cost: charged credits for the data it moves, like every endpoint. Also accepts POST with a JSON body.
Query parameters
Name | Type | Required | Description |
|---|---|---|---|
| string | — | The search terms. Required unless |
| string | — | A Macy's category id (the number after |
| integer | — | Results page, 1–100. Default |
| string | — | Result order. Each site offers only the orders it supports. One of: |
Request
- cURL
- Python
- Node.js
curl -G "https://api.scrapelyserp.com/v1/structured/macys/search" \
-H "Authorization: Bearer $SCRAPELY_API_KEY" \
--data-urlencode "query=dress shirt"import os
import requests
HEADERS = {"Authorization": f"Bearer {os.environ['SCRAPELY_API_KEY']}"}
r = requests.get(
"https://api.scrapelyserp.com/v1/structured/macys/search",
headers=HEADERS,
params={"query": "dress shirt"},
timeout=30,
)
r.raise_for_status()
data = r.json()const res = await fetch(`https://api.scrapelyserp.com/v1/structured/macys/search?${new URLSearchParams({ query: 'dress shirt' })}`, {
headers: { Authorization: `Bearer ${process.env.SCRAPELY_API_KEY}` },
signal: AbortSignal.timeout(30_000),
});
if (!res.ok) throw new Error((await res.json()).error.code);
const data = await res.json();Response
{
"endpoint": "macys/search",
"url": "https://www.macys.com/shop/featured/dress%20shirt",
"credits": 0.000028,
"duration_ms": 1480,
"data": {
"query": "dress shirt",
"page": 1,
"total_results": 849,
"next_page": 2,
"results": [
{
"position": 1,
"id": "18618230",
"title": "Men's Regular-Fit Heritage Oxford Shirt",
"url": "https://www.macys.com/shop/product/tommy-hilfiger-mens-regular-fit-heritage-oxford-shirt?ID=18618230",
"brand": "Tommy Hilfiger",
"image": null,
"price": null,
"price_range": {
"min": {
"amount": 28.43,
"currency": "USD"
},
"max": {
"amount": 89.5,
"currency": "USD"
}
},
"list_price": null,
"rating": 4.6,
"reviews_count": 175,
"in_stock": true
},
{
"position": 2,
"id": "26246806",
"title": "1200 Thread Count Cotton Blend Watercolor Printed 4-Piece Sheet Set",
"url": "https://www.macys.com/shop/product/macys-1200-thread-count-cotton-blend-watercolor-printed-4-piece-sheet-sets?ID=26246806",
"brand": "Macy's",
"image": null,
"price": {
"amount": 59.93,
"currency": "USD"
},
"price_range": null,
"list_price": {
"amount": 200,
"currency": "USD"
},
"rating": 4.3,
"reviews_count": 180,
"in_stock": true
}
]
}
}Responses
Status | Meaning |
|---|---|
| The macy's search as JSON. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Response fields
Field | Type | Description |
|---|---|---|
| string | — |
| string | The page we fetched. |
| number | What the call was charged, in credits, fractional (also |
| integer | — |
| object | — |
| string | — |
| integer | — |
| integer or null | — |
| integer or null | Send as |
| string or null | Poshmark only: send as |
| object[] | — |
GET /v1/structured/sephora/search
Sephora search.
Sephora search results for a query, in the site's order: id, title, brand, price, rating, review count, image and link. 200 per page; send next_page back as page for more. sort is best_match, price_low, price_high, top_rated, best_selling or newest. Each result is a product shown in one sku, and its id is the product id with that sku (P467208:2417145): it goes straight into POST /v1/products for Sephora. A product with several sizes or shades shows one card, so price is the LOWEST price shown and price_max the highest when no sale is running; when any sku is on sale price_max is null (the card does not say which skus are), and list_price is set only when the card shows one regular and one sale price. Ask for a sku with POST /v1/products for its exact price. rating and reviews_count are Sephora's own (null when unrated). in_stock is always null: search carries no stock. A keyword that matches nothing at all is an empty list (total_results 0), but a nonsense word usually still returns Sephora's closest products. A page past the last, and an answer that is not clearly a list of products or a no-match, is a failure (502 TARGET_FAILED, not charged, retry it), not an empty list.
Cost: charged credits for the data it moves, like every endpoint. Also accepts POST with a JSON body.
Query parameters
Name | Type | Required | Description |
|---|---|---|---|
| string | yes | The search terms. |
| integer | — | Results page, 1–100. Default |
| string | — | Result order. |
Request
- cURL
- Python
- Node.js
curl -G "https://api.scrapelyserp.com/v1/structured/sephora/search" \
-H "Authorization: Bearer $SCRAPELY_API_KEY" \
-d query=mascaraimport os
import requests
HEADERS = {"Authorization": f"Bearer {os.environ['SCRAPELY_API_KEY']}"}
r = requests.get(
"https://api.scrapelyserp.com/v1/structured/sephora/search",
headers=HEADERS,
params={"query": "mascara"},
timeout=30,
)
r.raise_for_status()
data = r.json()const res = await fetch(`https://api.scrapelyserp.com/v1/structured/sephora/search?${new URLSearchParams({ query: 'mascara' })}`, {
headers: { Authorization: `Bearer ${process.env.SCRAPELY_API_KEY}` },
signal: AbortSignal.timeout(30_000),
});
if (!res.ok) throw new Error((await res.json()).error.code);
const data = await res.json();Response
{
"endpoint": "sephora/search",
"url": "https://www.sephora.com/search?keyword=mascara",
"credits": 0.000028,
"duration_ms": 1480,
"data": {
"query": "mascara",
"page": 1,
"total_results": 196,
"next_page": null,
"results": [
{
"position": 1,
"id": "P467208:2417145",
"title": "Lash Idôle Lengthening & Volumizing Mascara",
"url": "https://www.sephora.com/product/lancome-lash-idole-lash-lifting-volumizing-mascara-P467208?skuId=2417145",
"brand": "Lancôme",
"image": "https://www.sephora.com/productimages/sku/s2417145-main-zoom.jpg?imwidth=270",
"price": {
"amount": 16,
"currency": "USD"
},
"price_max": {
"amount": 30,
"currency": "USD"
},
"list_price": null,
"rating": 4.5,
"reviews_count": 10787,
"in_stock": null
},
{
"position": 2,
"id": "P479863:2638336",
"title": "Lash Clash Extreme Volume Mascara",
"url": "https://www.sephora.com/product/yves-saint-laurent-lash-clash-extreme-volume-mascara-P479863?skuId=2638336",
"brand": "Yves Saint Laurent",
"image": "https://www.sephora.com/productimages/sku/s2638336-main-zoom.jpg?imwidth=270",
"price": {
"amount": 18,
"currency": "USD"
},
"price_max": {
"amount": 34,
"currency": "USD"
},
"list_price": null,
"rating": 4.12,
"reviews_count": 3607,
"in_stock": null
}
]
}
}Responses
Status | Meaning |
|---|---|
| The sephora search as JSON. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Response fields
Field | Type | Description |
|---|---|---|
| string | — |
| string | The page we fetched. |
| number | What the call was charged, in credits, fractional (also |
| integer | — |
| object | — |
| string | — |
| integer | — |
| integer or null | — |
| integer or null | Send as |
| string or null | Poshmark only: send as |
| object[] | — |
GET /v1/structured/newegg/search
Newegg category listing.
Newegg products listed by category, in Newegg's own order (by sales): item number, title, brand, price, list price, rating, review count, stock flag, link and image. Newegg has no keyword search here: query is refused, and no typeahead is offered. category is required and takes one of: components-storage, cpu, memory, motherboard, gpu, computer-case, power-supply, hard-drive, ssd, computer-systems, nas. 100 per page, up to page 75; send next_page back as page for more. sort is not offered. price is the price charged, after Newegg's instant rebate (the rebate is on POST /v1/products), and list_price the price before it when higher; a delisted product has no price. Marketplace sellers' offers are mixed in. rating and reviews_count are Newegg's own and null when unrated; in_stock is Newegg's online stock flag. About 4 in 100 of a category's products never appear when paging it. Each id is what POST /v1/products takes for Newegg. A page past the last, an empty answer, and an answer that is not clearly a list of products is a failure (502 TARGET_FAILED, not charged, retry it), not an empty list.
Cost: charged credits for the data it moves, like every endpoint. Also accepts POST with a JSON body.
Query parameters
Name | Type | Required | Description |
|---|---|---|---|
| string | yes | The category to list. Keyword search is not offered, and |
| integer | — | Results page, 1–75 (100 per page). Default |
Request
- cURL
- Python
- Node.js
curl -G "https://api.scrapelyserp.com/v1/structured/newegg/search" \
-H "Authorization: Bearer $SCRAPELY_API_KEY" \
-d category=cpuimport os
import requests
HEADERS = {"Authorization": f"Bearer {os.environ['SCRAPELY_API_KEY']}"}
r = requests.get(
"https://api.scrapelyserp.com/v1/structured/newegg/search",
headers=HEADERS,
params={"category": "cpu"},
timeout=30,
)
r.raise_for_status()
data = r.json()const res = await fetch(`https://api.scrapelyserp.com/v1/structured/newegg/search?${new URLSearchParams({ category: 'cpu' })}`, {
headers: { Authorization: `Bearer ${process.env.SCRAPELY_API_KEY}` },
signal: AbortSignal.timeout(30_000),
});
if (!res.ok) throw new Error((await res.json()).error.code);
const data = await res.json();Response
{
"endpoint": "newegg/search",
"url": "https://www.newegg.com/",
"credits": 0.000028,
"duration_ms": 1480,
"data": {
"query": "",
"page": 1,
"total_results": 102,
"next_page": 2,
"results": [
{
"position": 1,
"id": "19-113-877",
"title": "AMD Ryzen 7 9800X3D - Ryzen 7 9000 Series",
"url": "https://www.newegg.com/p/N82E16819113877",
"brand": "AMD",
"image": "https://c1.neweggimages.com/NeweggImage/ProductImageCompressAll200/19-113-877-01.png",
"price": {
"amount": 479,
"currency": "USD"
},
"list_price": null,
"rating": 4.8,
"reviews_count": 745,
"in_stock": true
}
]
}
}Responses
Status | Meaning |
|---|---|
| The newegg category listing as JSON. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Response fields
Field | Type | Description |
|---|---|---|
| string | — |
| string | The page we fetched. |
| number | What the call was charged, in credits, fractional (also |
| integer | — |
| object | — |
| string | — |
| integer | — |
| integer or null | — |
| integer or null | Send as |
| string or null | Poshmark only: send as |
| object[] | — |
GET /v1/structured/ulta/search
Ulta Beauty search.
Ulta Beauty search results for a query, in the site's order: item id, title, brand, price, regular price, rating, review count, image and link. 64 per page; send next_page back as page for more. sort is best_match, best_selling, newest, price_low, price_high or rating. Each result is a product shown in one shade or size, and its id is the product id and that sku joined by a colon: it goes straight into POST /v1/products for Ulta, which returns that sku's price and stock. A product whose shades or sizes cost different amounts has no price and a price_range instead. list_price is the regular price of a product marked down. in_stock is always null: search carries no stock. Sponsored placements are left out. rating is a pure rating order (five stars with few reviews first), not a quality ranking. A phrase that matches nothing is an empty list, though a nonsense phrase can still return a few unrelated products. A page past the last is an empty list with the total. An answer that is not clearly a list of products or a count of none is a failure (502 TARGET_FAILED, not charged, retry it), not an empty list.
Cost: charged credits for the data it moves, like every endpoint. Also accepts POST with a JSON body.
Query parameters
Name | Type | Required | Description |
|---|---|---|---|
| string | yes | The search terms. |
| integer | — | Results page, 1–100. Default |
| string | — | Result order. Each site offers only the orders it supports. One of: |
Request
- cURL
- Python
- Node.js
curl -G "https://api.scrapelyserp.com/v1/structured/ulta/search" \
-H "Authorization: Bearer $SCRAPELY_API_KEY" \
--data-urlencode "query=retinol serum"import os
import requests
HEADERS = {"Authorization": f"Bearer {os.environ['SCRAPELY_API_KEY']}"}
r = requests.get(
"https://api.scrapelyserp.com/v1/structured/ulta/search",
headers=HEADERS,
params={"query": "retinol serum"},
timeout=30,
)
r.raise_for_status()
data = r.json()const res = await fetch(`https://api.scrapelyserp.com/v1/structured/ulta/search?${new URLSearchParams({ query: 'retinol serum' })}`, {
headers: { Authorization: `Bearer ${process.env.SCRAPELY_API_KEY}` },
signal: AbortSignal.timeout(30_000),
});
if (!res.ok) throw new Error((await res.json()).error.code);
const data = await res.json();Response
{
"endpoint": "ulta/search",
"url": "https://www.ulta.com/search?search=retinol%20serum",
"credits": 0.000028,
"duration_ms": 1480,
"data": {
"query": "retinol serum",
"page": 1,
"total_results": 338,
"next_page": 2,
"results": [
{
"position": 1,
"id": "mkt77006708:77010477",
"title": "Nightinol Cream",
"url": "https://www.ulta.com/p/nightinol-cream-mkt77006708?sku=77010477",
"brand": "WYLYS",
"image": "https://media.ultainc.com/i/ulta/77010477",
"price": {
"amount": 215,
"currency": "USD"
},
"price_range": null,
"list_price": null,
"rating": null,
"reviews_count": null,
"in_stock": null
}
]
}
}Responses
Status | Meaning |
|---|---|
| The ulta beauty search as JSON. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Response fields
Field | Type | Description |
|---|---|---|
| string | — |
| string | The page we fetched. |
| number | What the call was charged, in credits, fractional (also |
| integer | — |
| object | — |
| string | — |
| integer | — |
| integer or null | — |
| integer or null | Send as |
| string or null | Poshmark only: send as |
| object[] | — |
GET /v1/structured/whatnot/search
Whatnot search.
Whatnot listings for a keyword, in the site's order: listing id, title, subtitle, price, buy-it-now or auction format, current bid for an auction, quantity, state, the seller's username, rating and review count, and link. Up to 50 per page; page on with cursor: send next_cursor back as cursor. sort is best_match, newest (newly listed), price_low or price_high; format keeps buy-it-now or auction listings. price is the listing price in dollars, never including shipping or tax; for an auction it is the listing price, not a bid. A price of 900,000 dollars or more is a seller's placeholder and is returned as null (whatnot.placeholderPrice is then true). rating and reviews_count are always null: a listing has none. The rating under whatnot.seller is the SELLER's, across all their sales, not the item's. Pictures are left out (image is null) unless images=true, which makes a page about nine times as costly. Search lists only listings for sale now; POST /v1/products finds a listing by id, a sold or ended one too. Each id is what POST /v1/products takes for Whatnot. A keyword that matches nothing is an empty list (total_results 0); an answer that is not clearly a list of listings or a total of zero is a failure (502 TARGET_FAILED, not charged, retry it), not an empty list.
Cost: charged credits for the data it moves, like every endpoint. Also accepts POST with a JSON body.
Query parameters
Name | Type | Required | Description |
|---|---|---|---|
| string | yes | The search terms. |
| string | — | The |
| string | — | Result order. |
| string | — | Only buy-it-now listings, or only auction listings. One of: |
| boolean | — | Return the first picture as |
Request
- cURL
- Python
- Node.js
curl -G "https://api.scrapelyserp.com/v1/structured/whatnot/search" \
-H "Authorization: Bearer $SCRAPELY_API_KEY" \
--data-urlencode "query=nike shoes"import os
import requests
HEADERS = {"Authorization": f"Bearer {os.environ['SCRAPELY_API_KEY']}"}
r = requests.get(
"https://api.scrapelyserp.com/v1/structured/whatnot/search",
headers=HEADERS,
params={"query": "nike shoes"},
timeout=30,
)
r.raise_for_status()
data = r.json()const res = await fetch(`https://api.scrapelyserp.com/v1/structured/whatnot/search?${new URLSearchParams({ query: 'nike shoes' })}`, {
headers: { Authorization: `Bearer ${process.env.SCRAPELY_API_KEY}` },
signal: AbortSignal.timeout(30_000),
});
if (!res.ok) throw new Error((await res.json()).error.code);
const data = await res.json();Response
{
"endpoint": "whatnot/search",
"url": "https://www.whatnot.com/search?query=nike%20shoes",
"credits": 0.000028,
"duration_ms": 1480,
"data": {
"query": "nike shoes",
"page": 1,
"total_results": 10000,
"next_page": null,
"next_cursor": "YXJyYXljb25uZWN0aW9uOjQ5",
"results": [
{
"position": 1,
"id": "TGlzdGluZ05vZGU6MjUwNTM3MTY3NQ==",
"title": "Size 10.5m/12w - Nike Vomero Premium 'Black Hot Lava' Womens Running Shoes NEW",
"url": "https://www.whatnot.com/listing/TGlzdGluZ05vZGU6MjUwNTM3MTY3NQ==",
"brand": null,
"image": null,
"price": {
"amount": 180,
"currency": "USD"
},
"list_price": null,
"rating": null,
"reviews_count": null,
"in_stock": true,
"whatnot": {
"subtitle": null,
"format": "buy_it_now",
"status": "active",
"quantity": 1,
"currentBid": null,
"bidCount": null,
"seller": {
"username": "crenyco_snkrs",
"rating": 5,
"reviewCount": 45
},
"placeholderPrice": false
}
},
{
"position": 2,
"id": "TGlzdGluZ05vZGU6MjQ3OTY5MTA2MA==",
"title": "S06. Nike Watermelon slides",
"url": "https://www.whatnot.com/listing/TGlzdGluZ05vZGU6MjQ3OTY5MTA2MA==",
"brand": null,
"image": null,
"price": {
"amount": 5,
"currency": "USD"
},
"list_price": null,
"rating": null,
"reviews_count": null,
"in_stock": true,
"whatnot": {
"subtitle": "8C ∙ Nike",
"format": "auction",
"status": "active",
"quantity": 1,
"currentBid": null,
"bidCount": null,
"seller": {
"username": "julesbegules",
"rating": 5,
"reviewCount": 1521
},
"placeholderPrice": false
}
}
]
}
}Responses
Status | Meaning |
|---|---|
| The whatnot search as JSON. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Response fields
Field | Type | Description |
|---|---|---|
| string | — |
| string | The page we fetched. |
| number | What the call was charged, in credits, fractional (also |
| integer | — |
| object | — |
| string | — |
| integer | — |
| integer or null | — |
| integer or null | Send as |
| string or null | Poshmark only: send as |
| object[] | — |
GET /v1/structured/kroger/search
Kroger search.
Kroger products for a keyword, best match first: UPC, title, brand, size, regular price, sale price (with its condition), multi-buy, price per unit, stock and pickup flag at the store, rating, review count, link and image. 24 results per request, with no paging and no sort: page and sort are refused. To see beyond the first 24, add brand (the exact brand name, as a result's brand writes it): total_results says how many Kroger has for the query (up to 1,000). Prices and stock are those of one store, the one Kroger picks for the request, reported in kroger.store (locationId, banner, city, state). Send zip to steer the request to that area: it picks the region, not an exact store, so read kroger.store for the store that was priced. price is the regular price of one unit; a sale is in kroger.promo, flagged conditional when it needs a purchase such as buying 3 or more. A keyword that matches nothing is an empty list (total_results 0); an answer that is not clearly a list of products or a total of zero is a failure (502 TARGET_FAILED, not charged, retry it), not an empty list. Each id is what POST /v1/products takes for Kroger.
Cost: charged credits for the data it moves, like every endpoint. Also accepts POST with a JSON body.
Query parameters
Name | Type | Required | Description |
|---|---|---|---|
| string | yes | The search terms. |
| string | — | Only products of this brand, written exactly as Kroger's |
| string | — | Which Kroger-family store brand to search: |
| string | — | A 5-digit US ZIP code. Steers the request to that area, so the store priced is one near there, not necessarily the nearest to the ZIP: |
Request
- cURL
- Python
- Node.js
curl -G "https://api.scrapelyserp.com/v1/structured/kroger/search" \
-H "Authorization: Bearer $SCRAPELY_API_KEY" \
--data-urlencode "query=cinnamon toast crunch"import os
import requests
HEADERS = {"Authorization": f"Bearer {os.environ['SCRAPELY_API_KEY']}"}
r = requests.get(
"https://api.scrapelyserp.com/v1/structured/kroger/search",
headers=HEADERS,
params={"query": "cinnamon toast crunch"},
timeout=30,
)
r.raise_for_status()
data = r.json()const res = await fetch(`https://api.scrapelyserp.com/v1/structured/kroger/search?${new URLSearchParams({ query: 'cinnamon toast crunch' })}`, {
headers: { Authorization: `Bearer ${process.env.SCRAPELY_API_KEY}` },
signal: AbortSignal.timeout(30_000),
});
if (!res.ok) throw new Error((await res.json()).error.code);
const data = await res.json();Response
{
"endpoint": "kroger/search",
"url": "https://www.kroger.com/search?query=cinnamon%20toast%20crunch",
"credits": 0.000028,
"duration_ms": 1480,
"data": {
"query": "cinnamon toast crunch",
"page": 1,
"total_results": 11,
"next_page": null,
"results": [
{
"position": 1,
"id": "0001600012254",
"title": "General Mills Cinnamon Toast Crunch™ Cereal",
"url": "https://www.kroger.com/p/general-mills-cinnamon-toast-crunch-cereal/0001600012254",
"brand": "General Mills",
"image": "https://www.kroger.com/product/images/large/front/0001600012254",
"price": {
"amount": 3.99,
"currency": "USD"
},
"list_price": null,
"rating": 4.8,
"reviews_count": 2476,
"in_stock": true,
"kroger": {
"size": "12 oz",
"soldBy": "unit",
"unitPrice": "$0.33/oz",
"multiBuy": null,
"promo": {
"price": {
"amount": 3.79,
"currency": "USD"
},
"expiresAt": "2026-11-18T04:59:59.999Z",
"conditional": true,
"condition": "Save $2 each when you buy 3 or more"
},
"stockLevel": "HIGH",
"available": 52,
"pickupSellable": true,
"variantGroupId": "1578351442723527"
}
}
],
"kroger": {
"banner": "kroger",
"store": {
"locationId": "01100439",
"banner": "kroger",
"city": "Waycross",
"state": "GA"
},
"brand": null,
"shown": 1
}
}
}Responses
Status | Meaning |
|---|---|
| The kroger search as JSON. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Response fields
Field | Type | Description |
|---|---|---|
| string | — |
| string | The page we fetched. |
| number | What the call was charged, in credits, fractional (also |
| integer | — |
| object | — |
| string | — |
| integer | — |
| integer or null | — |
| integer or null | Send as |
| string or null | Poshmark only: send as |
| object[] | — |
GET /v1/structured/homedepot/search
Home Depot search.
Home Depot Canada products for a keyword, in the site's order: item number, title, brand, price in Canadian dollars, earlier price, rating, review count, stock, model, aisle and bay, link and image. Up to 40 per page; send next_page back as page for more. Prices and stock are those of one store: send store (a store number such as 7222) or postal_code (a Canadian postal code: the store nearest to it is used), and read homedepot.store for the store that was priced; with neither, the default store is used (Fort Saskatchewan, AB). A store number that does not exist, or a postal code with no store near it, is a free 404. sort is relevance, price_low, price_high or rating. price is the price at the store; list_price is the earlier price of a marked-down product, null otherwise. rating and reviews_count are Home Depot's own and null for a product with no reviews. in_stock is true when the product is in stock in the store or online, false when it is out in both, null when neither is given; the unit counts are homedepot.stockAtStore and homedepot.stockOnline. Each id is what POST /v1/products takes for Home Depot. A keyword that matches nothing is not an empty list: Home Depot answers with one loosely matching product (total_results 1). The list is empty only when total_results is 0, and an answer that is not clearly a list of products or a total of zero is a failure (502 TARGET_FAILED, not charged, retry it).
Cost: charged credits for the data it moves, like every endpoint. Also accepts POST with a JSON body.
Query parameters
Name | Type | Required | Description |
|---|---|---|---|
| string | yes | The search terms. |
| integer | — | Results page, 1–250 (40 products each). Default |
| string | — | Result order: |
| string | — | A Home Depot store number ( |
| string | — | A Canadian postal code ( |
Request
- cURL
- Python
- Node.js
curl -G "https://api.scrapelyserp.com/v1/structured/homedepot/search" \
-H "Authorization: Bearer $SCRAPELY_API_KEY" \
-d query=drillimport os
import requests
HEADERS = {"Authorization": f"Bearer {os.environ['SCRAPELY_API_KEY']}"}
r = requests.get(
"https://api.scrapelyserp.com/v1/structured/homedepot/search",
headers=HEADERS,
params={"query": "drill"},
timeout=30,
)
r.raise_for_status()
data = r.json()const res = await fetch(`https://api.scrapelyserp.com/v1/structured/homedepot/search?${new URLSearchParams({ query: 'drill' })}`, {
headers: { Authorization: `Bearer ${process.env.SCRAPELY_API_KEY}` },
signal: AbortSignal.timeout(30_000),
});
if (!res.ok) throw new Error((await res.json()).error.code);
const data = await res.json();Response
{
"endpoint": "homedepot/search",
"url": "https://www.homedepot.ca/search?q=drill",
"credits": 0.000028,
"duration_ms": 1480,
"data": {
"query": "drill",
"page": 1,
"total_results": 4232,
"next_page": 2,
"results": [
{
"position": 1,
"id": "1001686659",
"title": "18V ONE+ Cordless 1/2 in. Drill/Driver Kit with (1) 1.5 Ah Battery and Charger",
"url": "https://www.homedepot.ca/product/ryobi-18v-one-cordless-1-2-in-drill-driver-kit-with-1-1-5-ah-battery-and-charger/1001686659",
"brand": "RYOBI",
"image": "https://images.homedepot.ca/productimages/p_1001686659.jpg",
"price": {
"amount": 78,
"currency": "CAD"
},
"list_price": null,
"rating": 4.76,
"reviews_count": 2533,
"in_stock": true,
"homedepot": {
"model": "PCL206K1X",
"originalPrice": null,
"savingsPercent": null,
"stockAtStore": {
"status": "in_stock",
"quantity": 15
},
"stockOnline": {
"status": "in_stock",
"quantity": 389
},
"aisle": "9",
"bay": "001"
}
},
{
"position": 2,
"id": "1001769719",
"title": "20V MAX ATOMIC Lithium-Ion Cordless Brushless Compact 1/2-inch Drill/Driver Kit - with 2.0Ah Battery, Charger & Bag",
"url": "https://www.homedepot.ca/product/dewalt-20v-max-atomic-lithium-ion-cordless-brushless-compact-1-2-inch-drill-driver-kit-with-2-0ah-battery-charger-bag/1001769719",
"brand": "DEWALT",
"image": "https://images.homedepot.ca/productimages/p_1001769719.jpg",
"price": {
"amount": 118,
"currency": "CAD"
},
"list_price": {
"amount": 219,
"currency": "CAD"
},
"rating": 4.74,
"reviews_count": 5997,
"in_stock": true,
"homedepot": {
"model": "DCD794D1",
"originalPrice": {
"amount": 219,
"currency": "CAD"
},
"savingsPercent": 46,
"stockAtStore": {
"status": "in_stock",
"quantity": 34
},
"stockOnline": {
"status": "in_stock",
"quantity": 107
},
"aisle": "10",
"bay": "004"
}
}
],
"homedepot": {
"store": {
"id": "7222",
"name": "FORT SASKATCHEWAN",
"city": "Fort Saskatchewan",
"province": "AB"
}
}
}
}Responses
Status | Meaning |
|---|---|
| The home depot search as JSON. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Response fields
Field | Type | Description |
|---|---|---|
| string | — |
| string | The page we fetched. |
| number | What the call was charged, in credits, fractional (also |
| integer | — |
| object | — |
| string | — |
| integer | — |
| integer or null | — |
| integer or null | Send as |
| string or null | Poshmark only: send as |
| object[] | — |
GET /v1/structured/wayfair/search
Wayfair search.
Wayfair products for a keyword: sku, name, brand, price, list price, rating, review count, stock, link and image. Up to 100 per request (size, 48 by default); send next_page back as page for more. sort is best_match, price_low, price_high or top_rated; sale=true keeps products that are marked down. Results are always returned: a keyword that matches nothing gets Wayfair's popular products instead, so an empty list never means "no match", and total_results is always null because Wayfair gives no reliable match count. Only the first 1,920 results of a query can be reached (page × size at most 1,920). Sponsored products are included and flagged, and can shift between pages, so a product can appear on two pages. price is the price shown on the card and list_price the struck-through price, null when there is none. rating and reviews_count are Wayfair's own and null for a product with no reviews (never 0). in_stock is true or false only when Wayfair says so and null when it does not; wayfair.stock_status is in_stock, low_stock (in stock, but low) or out_of_stock, and wayfair.stock_message is the card's own text ("Low Stock", "13 Left in Stock"). Prices are those for one fixed delivery area. A busy moment can fail a call (502 TARGET_FAILED, not charged): retry it.
Cost: charged credits for the data it moves, like every endpoint. Also accepts POST with a JSON body.
Query parameters
Name | Type | Required | Description |
|---|---|---|---|
| string | yes | The search terms. |
| integer | — | Results page; page × size must be at most 1920. Default |
| integer | — | Results per page, 1–100. A page costs in proportion to its size. Default |
| string | — | Result order: |
| boolean | — | Only products that are marked down. Default |
Request
- cURL
- Python
- Node.js
curl -G "https://api.scrapelyserp.com/v1/structured/wayfair/search" \
-H "Authorization: Bearer $SCRAPELY_API_KEY" \
--data-urlencode "query=standing desk lamp"import os
import requests
HEADERS = {"Authorization": f"Bearer {os.environ['SCRAPELY_API_KEY']}"}
r = requests.get(
"https://api.scrapelyserp.com/v1/structured/wayfair/search",
headers=HEADERS,
params={"query": "standing desk lamp"},
timeout=30,
)
r.raise_for_status()
data = r.json()const res = await fetch(`https://api.scrapelyserp.com/v1/structured/wayfair/search?${new URLSearchParams({ query: 'standing desk lamp' })}`, {
headers: { Authorization: `Bearer ${process.env.SCRAPELY_API_KEY}` },
signal: AbortSignal.timeout(30_000),
});
if (!res.ok) throw new Error((await res.json()).error.code);
const data = await res.json();Response
{
"endpoint": "wayfair/search",
"url": "https://www.wayfair.com/keyword.php?keyword=standing%20desk%20lamp",
"credits": 0.000028,
"duration_ms": 1480,
"data": {
"query": "standing desk lamp",
"page": 1,
"total_results": null,
"next_page": 2,
"results": [
{
"position": 1,
"id": "W005077765",
"title": "Dwayne 68.5'' Dimmable LED Task/Reading Floor Lamp with Remote & Touch Control",
"url": "https://www.wayfair.com/lighting/pdp/ivy-bronx-dwayne-685-dimmable-led-taskreading-floor-lamp-with-remote-touch-control-w005077765.html?piid=129541178",
"brand": "Ivy Bronx",
"image": "https://secure.img1-fg.wfcdn.com/im/00000000/resize-h400-w400%5Ecompr-r85/2151/215129689/image.jpg",
"price": {
"amount": 55.99,
"currency": "USD"
},
"list_price": {
"amount": 79.99,
"currency": "USD"
},
"rating": 4.69,
"reviews_count": 1385,
"in_stock": true,
"sponsored": false,
"wayfair": {
"stock_status": "in_stock",
"stock_message": "13 Left in Stock"
}
},
{
"position": 2,
"id": "W006666886",
"title": "Jernell 70\" LED Dimmable Torchiere & Reading Floor Lamp with Remote Control",
"url": "https://www.wayfair.com/lighting/pdp/latitude-run-jernell-70-led-dimmable-torchiere-reading-floor-lamp-with-remote-control-w006666886.html?piid=1270286859",
"brand": "Latitude Run®",
"image": "https://secure.img1-fg.wfcdn.com/im/00000000/resize-h400-w400%5Ecompr-r85/2697/269735487/image.jpg",
"price": {
"amount": 72.99,
"currency": "USD"
},
"list_price": {
"amount": 110,
"currency": "USD"
},
"rating": 4.61,
"reviews_count": 1669,
"in_stock": false,
"sponsored": false,
"wayfair": {
"stock_status": "out_of_stock",
"stock_message": "Out of Stock"
}
}
]
}
}Responses
Status | Meaning |
|---|---|
| The wayfair search as JSON. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Response fields
Field | Type | Description |
|---|---|---|
| string | — |
| string | The page we fetched. |
| number | What the call was charged, in credits, fractional (also |
| integer | — |
| object | — |
| string | — |
| integer | — |
| integer or null | — |
| integer or null | Send as |
| string or null | Poshmark only: send as |
| object[] | — |
GET /v1/structured/shopify/catalog
Shopify store catalog.
A page of a Shopify store's products, in the store's own order: product id (<hostname>/<handle>), title, vendor (as brand), product type, tags, link, first image, lowest variant price, compare-at price, each variant's price and availability flag, and publish and update dates. Up to 250 products per page; page moves through the catalog, and collection (a collection handle) lists one collection instead. query filters the page you fetched, it does not search the store: the store has no keyword search, so only the products of that page whose title, vendor, product type or tags contain every word are returned. Walk the pages to search a whole store. query echoes the filter (empty when there is none), total_results is always null (the store does not say how many products it has), and next_page is set only when the page was full, so the page after the last one is empty. Prices carry no currency (currency is null): the store's own currency applies. Each row also has a shopify object: handle, product id, vendor, product type, tags, every variant (id, sku, title, price, compareAtPrice, available) and the publish and update dates. price is the lowest variant price and list_price the compare-at price of that variant when it is higher, else null. in_stock is true when any variant is available, false when none is, and null when the store does not say; quantities are never given. A store that is not on Shopify, keeps its catalog private or is password protected is a free 404 NOT_FOUND; an answer that is not a product list is a failure (502 TARGET_FAILED, not charged, retry it). Each id is what POST /v1/products takes for Shopify stores.
Cost: charged credits for the data it moves, like every endpoint. Also accepts POST with a JSON body.
Query parameters
Name | Type | Required | Description |
|---|---|---|---|
| string | yes | The store hostname in lowercase, e.g. |
| string | — | A collection handle (the part after /collections/ in the store's link): list that collection instead of the whole store. An unknown collection is a 404. |
| integer | — | Catalog page, 1–1000 (250 products each). An empty page is the end. Default |
| integer | — | Products per page, 1–250. Default |
| string | — | Keep only the products of this page whose title, vendor, product type or tags contain every word. It filters one page, not the store. |
Request
- cURL
- Python
- Node.js
curl -G "https://api.scrapelyserp.com/v1/structured/shopify/catalog" \
-H "Authorization: Bearer $SCRAPELY_API_KEY" \
-d domain=allbirds.comimport os
import requests
HEADERS = {"Authorization": f"Bearer {os.environ['SCRAPELY_API_KEY']}"}
r = requests.get(
"https://api.scrapelyserp.com/v1/structured/shopify/catalog",
headers=HEADERS,
params={"domain": "allbirds.com"},
timeout=30,
)
r.raise_for_status()
data = r.json()const res = await fetch(`https://api.scrapelyserp.com/v1/structured/shopify/catalog?${new URLSearchParams({ domain: 'allbirds.com' })}`, {
headers: { Authorization: `Bearer ${process.env.SCRAPELY_API_KEY}` },
signal: AbortSignal.timeout(30_000),
});
if (!res.ok) throw new Error((await res.json()).error.code);
const data = await res.json();Response
{
"endpoint": "shopify/catalog",
"url": "https://undefeated.com/collections/all",
"credits": 0.000028,
"duration_ms": 1480,
"data": {
"query": "",
"page": 1,
"total_results": null,
"next_page": 2,
"results": [
{
"position": 1,
"id": "undefeated.com/adidas-wmns-taekwondo-mei-ballet-sanpin-owhite-gum5",
"title": "ADIDAS WMNS TAEKWONDO MEI BALLET - SANPIN/ OWHITE/ GUM5",
"url": "https://undefeated.com/products/adidas-wmns-taekwondo-mei-ballet-sanpin-owhite-gum5",
"brand": "ADIDAS",
"image": "https://cdn.shopify.com/s/files/1/0282/5850/files/footwear_adidas_wmns-taekwondo-mei-ballet_IH6915.view_1.jpg?v=1771891297",
"price": {
"amount": 47.5,
"currency": null
},
"list_price": {
"amount": 95,
"currency": null
},
"in_stock": true,
"shopify": {
"domain": "undefeated.com",
"handle": "adidas-wmns-taekwondo-mei-ballet-sanpin-owhite-gum5",
"productId": 6702088290565,
"vendor": "ADIDAS",
"productType": "Womens Footwear",
"tags": ["26.Q1", "adidas", "MEMORIAL_26", "size-8-5", "womens-footwear"],
"variants": [
{
"id": 39751807336709,
"sku": "IH6915---6",
"title": "6",
"price": 47.5,
"compareAtPrice": 95,
"available": false
},
{
"id": 39751807369477,
"sku": "IH6915---6.5",
"title": "6.5",
"price": 47.5,
"compareAtPrice": 95,
"available": false
}
],
"publishedAt": "2026-11-04T17:22:07.000Z",
"updatedAt": "2026-11-05T19:27:03.000Z"
}
}
]
}
}Responses
Status | Meaning |
|---|---|
| The shopify store catalog as JSON. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Response fields
Field | Type | Description |
|---|---|---|
| string | — |
| string | The page we fetched. |
| number | What the call was charged, in credits, fractional (also |
| integer | — |
| object | — |
| string | — |
| integer | — |
| integer or null | — |
| integer or null | Send as |
| string or null | Poshmark only: send as |
| object[] | — |
GET /v1/structured/reddit/posts
Reddit posts.
A subreddit's posts, newest (or hot, top, rising) first: title, author, score, comment count, upvote ratio, post type, flair and text. About 25 per page; page on with cursor.
Cost: charged credits for the data it moves, like every endpoint. Also accepts POST with a JSON body.
Query parameters
Name | Type | Required | Description |
|---|---|---|---|
| string | yes | The subreddit, e.g. |
| string | — | Listing order. One of: |
| string | — | With |
| string | — | The |
Request
- cURL
- Python
- Node.js
curl -G "https://api.scrapelyserp.com/v1/structured/reddit/posts" \
-H "Authorization: Bearer $SCRAPELY_API_KEY" \
-d subreddit=webscraping \
-d sort=newimport os
import requests
HEADERS = {"Authorization": f"Bearer {os.environ['SCRAPELY_API_KEY']}"}
r = requests.get(
"https://api.scrapelyserp.com/v1/structured/reddit/posts",
headers=HEADERS,
params={"subreddit": "webscraping", "sort": "new"},
timeout=30,
)
r.raise_for_status()
data = r.json()const res = await fetch(`https://api.scrapelyserp.com/v1/structured/reddit/posts?${new URLSearchParams({ subreddit: 'webscraping', sort: 'new' })}`, {
headers: { Authorization: `Bearer ${process.env.SCRAPELY_API_KEY}` },
signal: AbortSignal.timeout(30_000),
});
if (!res.ok) throw new Error((await res.json()).error.code);
const data = await res.json();Response
{
"endpoint": "reddit/posts",
"url": "https://www.reddit.com/r/webscraping/new/",
"credits": 0.000028,
"duration_ms": 1480,
"data": {
"subreddit": "webscraping",
"sort": "new",
"time": null,
"posts": [
{
"id": "1xn96sv",
"subreddit": "webscraping",
"title": "Anyone doing webscraping with an antidetect browser?",
"author": "example_user",
"url": "https://www.reddit.com/r/webscraping/comments/1xn96sv/…/",
"link": null,
"created_at": "2026-11-03T23:51:15.577Z",
"score": 5,
"comments": 8,
"upvote_ratio": 1,
"type": "text",
"domain": "self.webscraping",
"flair": "Getting started",
"text": "Hello. I would like to add some plugins for scraping…"
}
],
"next_cursor": "dDNfMXhncncybg=="
}
}Responses
Status | Meaning |
|---|---|
| The reddit posts as JSON. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Response fields
Field | Type | Description |
|---|---|---|
| string | — |
| string | The page we fetched. |
| number | What the call was charged, in credits, fractional (also |
| integer | — |
| object | — |
| string | — |
| string | — |
| string or null | — |
| object[] | — |
| string or null | Pass as |
GET /v1/structured/reddit/search
Reddit search.
Posts matching a query, across Reddit or in one subreddit: title, author, subreddit, time and text. Up to 100 per page; page on with cursor. Sort by new to watch for mentions.
Cost: charged credits for the data it moves, like every endpoint. Also accepts POST with a JSON body.
Query parameters
Name | Type | Required | Description |
|---|---|---|---|
| string | yes | The search terms. |
| string | — | Search only this subreddit, e.g. |
| string | — | Result order. One of: |
| string | — | Only posts from this window. One of: |
| integer | — | Results per page, 1–100. Default |
| string | — | The |
Request
- cURL
- Python
- Node.js
curl -G "https://api.scrapelyserp.com/v1/structured/reddit/search" \
-H "Authorization: Bearer $SCRAPELY_API_KEY" \
--data-urlencode "query=web scraping api" \
-d sort=newimport os
import requests
HEADERS = {"Authorization": f"Bearer {os.environ['SCRAPELY_API_KEY']}"}
r = requests.get(
"https://api.scrapelyserp.com/v1/structured/reddit/search",
headers=HEADERS,
params={"query": "web scraping api", "sort": "new"},
timeout=30,
)
r.raise_for_status()
data = r.json()const res = await fetch(`https://api.scrapelyserp.com/v1/structured/reddit/search?${new URLSearchParams({ query: 'web scraping api', sort: 'new' })}`, {
headers: { Authorization: `Bearer ${process.env.SCRAPELY_API_KEY}` },
signal: AbortSignal.timeout(30_000),
});
if (!res.ok) throw new Error((await res.json()).error.code);
const data = await res.json();Response
{
"endpoint": "reddit/search",
"url": "https://www.reddit.com/search/?q=web+scraping+api&sort=new",
"credits": 0.000028,
"duration_ms": 1480,
"data": {
"query": "web scraping api",
"subreddit": null,
"sort": "new",
"time": "all",
"results": [
{
"id": "1xnfagb",
"subreddit": "indiehackers",
"title": "Frustrated with SEO tool pricing, so I built this",
"author": "example_user",
"url": "https://www.reddit.com/r/indiehackers/comments/1xnfagb/…/",
"created_at": "2026-11-04T04:48:21.000Z",
"text": "I have been on the starter plan for a year…"
}
],
"next_cursor": "t3_1xnfagb"
}
}Responses
Status | Meaning |
|---|---|
| The reddit search as JSON. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Response fields
Field | Type | Description |
|---|---|---|
| string | — |
| string | The page we fetched. |
| number | What the call was charged, in credits, fractional (also |
| integer | — |
| object | — |
| string | — |
| string or null | — |
| string | — |
| string or null | — |
| object[] | — |
| string or null | Pass as |
GET /v1/structured/reddit/comments
Reddit comments.
A post's comments (send post), or a subreddit's newest comments (send subreddit): author, time, text and the post each one is on. Up to 100 per page.
Cost: charged credits for the data it moves, like every endpoint. Also accepts POST with a JSON body.
Query parameters
Name | Type | Required | Description |
|---|---|---|---|
| string | — | A post's id (e.g. |
| string | — | A subreddit, e.g. |
| string | — | With |
| integer | — | Results per page, 1–100. Default |
| string | — | With |
Request
- cURL
- Python
- Node.js
curl -G "https://api.scrapelyserp.com/v1/structured/reddit/comments" \
-H "Authorization: Bearer $SCRAPELY_API_KEY" \
-d subreddit=webscrapingimport os
import requests
HEADERS = {"Authorization": f"Bearer {os.environ['SCRAPELY_API_KEY']}"}
r = requests.get(
"https://api.scrapelyserp.com/v1/structured/reddit/comments",
headers=HEADERS,
params={"subreddit": "webscraping"},
timeout=30,
)
r.raise_for_status()
data = r.json()const res = await fetch(`https://api.scrapelyserp.com/v1/structured/reddit/comments?${new URLSearchParams({ subreddit: 'webscraping' })}`, {
headers: { Authorization: `Bearer ${process.env.SCRAPELY_API_KEY}` },
signal: AbortSignal.timeout(30_000),
});
if (!res.ok) throw new Error((await res.json()).error.code);
const data = await res.json();Response
{
"endpoint": "reddit/comments",
"url": "https://www.reddit.com/r/webscraping/comments/",
"credits": 0.000028,
"duration_ms": 1480,
"data": {
"subreddit": "webscraping",
"post": null,
"comments": [
{
"id": "19s3yr8g8su0x",
"post_id": "1xn96sv",
"post_title": "Anyone doing webscraping with an antidetect browser?",
"subreddit": "webscraping",
"author": "example_user",
"url": "https://www.reddit.com/r/webscraping/comments/1xn96sv/…/19s3yr8g8su0x/",
"created_at": "2026-11-04T03:45:28.000Z",
"text": "Python and Playwright here."
}
],
"next_cursor": "t1_19s3yr8g8su0x"
}
}Responses
Status | Meaning |
|---|---|
| The reddit comments as JSON. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Response fields
Field | Type | Description |
|---|---|---|
| string | — |
| string | The page we fetched. |
| number | What the call was charged, in credits, fractional (also |
| integer | — |
| object | — |
| string or null | Set for a subreddit's newest comments. |
| any | Set for one post's comments: the post itself. |
| object[] | — |
| string or null | Pass as |
GET /v1/structured/bing/search
Bing search.
A Bing web results page: each organic result with its position, title, URL, displayed URL and snippet, plus the estimated total. Ten per page.
Cost: charged credits for the data it moves, like every endpoint. Also accepts POST with a JSON body.
Query parameters
Name | Type | Required | Description |
|---|---|---|---|
| string | yes | The search terms. |
| integer | — | Results page, 1–100. Default |
| string | — | Two-letter country to search as. Default |
| string | — | Interface language, e.g. |
| string | — | A 5-digit US ZIP code. Results reflect that area (local prices, stock and nearby results). Unknown ZIP codes are a free 400. |
Request
- cURL
- Python
- Node.js
curl -G "https://api.scrapelyserp.com/v1/structured/bing/search" \
-H "Authorization: Bearer $SCRAPELY_API_KEY" \
--data-urlencode "query=web scraping api" \
-d country=usimport os
import requests
HEADERS = {"Authorization": f"Bearer {os.environ['SCRAPELY_API_KEY']}"}
r = requests.get(
"https://api.scrapelyserp.com/v1/structured/bing/search",
headers=HEADERS,
params={"query": "web scraping api", "country": "us"},
timeout=30,
)
r.raise_for_status()
data = r.json()const res = await fetch(`https://api.scrapelyserp.com/v1/structured/bing/search?${new URLSearchParams({ query: 'web scraping api', country: 'us' })}`, {
headers: { Authorization: `Bearer ${process.env.SCRAPELY_API_KEY}` },
signal: AbortSignal.timeout(30_000),
});
if (!res.ok) throw new Error((await res.json()).error.code);
const data = await res.json();Response
{
"endpoint": "bing/search",
"url": "https://www.bing.com/search?q=web+scraping+api",
"credits": 0.000028,
"duration_ms": 1480,
"data": {
"query": "web scraping api",
"page": 1,
"total_results": 70400,
"results": [
{
"position": 1,
"title": "Web Scraping API — Example",
"url": "https://www.example.com/",
"displayed_url": "https://www.example.com",
"snippet": "Public web data, from request to dataset…"
}
]
}
}Responses
Status | Meaning |
|---|---|
| The bing search as JSON. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Response fields
Field | Type | Description |
|---|---|---|
| string | — |
| string | The page we fetched. |
| number | What the call was charged, in credits, fractional (also |
| integer | — |
| object | A web results page: the organic results in page order. |
| string | — |
| integer | — |
| integer or null | The engine's own estimate, when it shows one. |
| object[] | — |
GET /v1/structured/duckduckgo/search
DuckDuckGo search.
A DuckDuckGo web results page: each organic result with its position, title, URL, displayed URL and snippet (ads left out). About 10 results on page 1 and 15 on later pages; pages 1–5.
Cost: charged credits for the data it moves, like every endpoint. Also accepts POST with a JSON body.
Query parameters
Name | Type | Required | Description |
|---|---|---|---|
| string | yes | The search terms. |
| integer | — | Results page, 1–5. A later page costs about as much as every page before it together. Default |
| string | — | Region and language, e.g. |
| string | — | A 5-digit US ZIP code. Results reflect that area (local prices, stock and nearby results). Unknown ZIP codes are a free 400. |
Request
- cURL
- Python
- Node.js
curl -G "https://api.scrapelyserp.com/v1/structured/duckduckgo/search" \
-H "Authorization: Bearer $SCRAPELY_API_KEY" \
--data-urlencode "query=web scraping api" \
-d region=us-enimport os
import requests
HEADERS = {"Authorization": f"Bearer {os.environ['SCRAPELY_API_KEY']}"}
r = requests.get(
"https://api.scrapelyserp.com/v1/structured/duckduckgo/search",
headers=HEADERS,
params={"query": "web scraping api", "region": "us-en"},
timeout=30,
)
r.raise_for_status()
data = r.json()const res = await fetch(`https://api.scrapelyserp.com/v1/structured/duckduckgo/search?${new URLSearchParams({ query: 'web scraping api', region: 'us-en' })}`, {
headers: { Authorization: `Bearer ${process.env.SCRAPELY_API_KEY}` },
signal: AbortSignal.timeout(30_000),
});
if (!res.ok) throw new Error((await res.json()).error.code);
const data = await res.json();Response
{
"endpoint": "duckduckgo/search",
"url": "https://duckduckgo.com/?q=web+scraping+api&kl=us-en",
"credits": 0.000028,
"duration_ms": 1480,
"data": {
"query": "web scraping api",
"page": 1,
"total_results": null,
"results": [
{
"position": 1,
"title": "15 Best Web Scraping APIs in 2026",
"url": "https://example.org/guides/best-web-scraping-apis/",
"displayed_url": "example.org/guides/best-web-scraping-apis/",
"snippet": "Which web scraping API handles JavaScript-heavy sites best?…"
}
]
}
}Responses
Status | Meaning |
|---|---|
| The duckduckgo search as JSON. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Response fields
Field | Type | Description |
|---|---|---|
| string | — |
| string | The page we fetched. |
| number | What the call was charged, in credits, fractional (also |
| integer | — |
| object | A web results page: the organic results in page order. |
| string | — |
| integer | — |
| integer or null | The engine's own estimate, when it shows one. |
| object[] | — |
GET /v1/structured/gemini/answer
Gemini answer.
Gemini's answer to a prompt as a visitor who is not signed in sees it: the answer text in markdown, the model, the sources it cites in order (each the page itself), whether it declined, and what it searched for when it says.
Cost: charged credits for the data it moves, like every endpoint. Also accepts POST with a JSON body.
Query parameters
Name | Type | Required | Description |
|---|---|---|---|
| string | yes | What to ask, up to 2,000 characters. |
| string | — | Two-letter country to ask from. Default |
| string | — | A 5-digit US ZIP code. Results reflect that area (local prices, stock and nearby results). Unknown ZIP codes are a free 400. |
Request
- cURL
- Python
- Node.js
curl -G "https://api.scrapelyserp.com/v1/structured/gemini/answer" \
-H "Authorization: Bearer $SCRAPELY_API_KEY" \
--data-urlencode "prompt=What is the best CRM for a small business?" \
-d country=usimport os
import requests
HEADERS = {"Authorization": f"Bearer {os.environ['SCRAPELY_API_KEY']}"}
r = requests.get(
"https://api.scrapelyserp.com/v1/structured/gemini/answer",
headers=HEADERS,
params={
"prompt": "What is the best CRM for a small business?",
"country": "us",
},
timeout=30,
)
r.raise_for_status()
data = r.json()const res = await fetch(`https://api.scrapelyserp.com/v1/structured/gemini/answer?${new URLSearchParams({ prompt: 'What is the best CRM for a small business?', country: 'us' })}`, {
headers: { Authorization: `Bearer ${process.env.SCRAPELY_API_KEY}` },
signal: AbortSignal.timeout(30_000),
});
if (!res.ok) throw new Error((await res.json()).error.code);
const data = await res.json();Response
{
"endpoint": "gemini/answer",
"url": "https://gemini.google.com/app",
"credits": 0.000028,
"duration_ms": 1480,
"data": {
"prompt": "What is the best CRM for a small business?",
"model": "3.5 Flash-Lite",
"text": "The best CRM depends on your team size and budget. **HubSpot CRM** has a generous free tier, **Zoho Bigin** suits micro-businesses, and **Pipedrive** is built around the sales pipeline.",
"citations": [
{
"position": 1,
"url": "https://www.pcmag.com/picks/the-best-crm-software",
"title": "The Best CRM Software We've Tested for 2026 | PCMag",
"domain": "pcmag.com"
},
{
"position": 2,
"url": "https://zapier.com/blog/best-crms-for-small-business/",
"title": "The 11 Best CRMs for Small Business in 2026 - Zapier",
"domain": "zapier.com"
}
],
"refused": false,
"search_queries": null
}
}Responses
Status | Meaning |
|---|---|
| The gemini answer as JSON. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Response fields
Field | Type | Description |
|---|---|---|
| string | — |
| string | The page we fetched. |
| number | What the call was charged, in credits, fractional (also |
| integer | — |
| object | An AI assistant's answer to a prompt, as a visitor sees it. |
| string | — |
| string or null | The model that answered, as the assistant names it. |
| string | The answer, markdown. |
| object[] | Sources in the order the assistant lists them, one per URL, each the page itself. |
| boolean | The assistant declined to answer. |
| any | What it searched for, when it says; null when it does not. |
GET /v1/structured/chatgpt/answer
ChatGPT answer.
The answer ChatGPT gives an anonymous visitor to a prompt, with web search: the text as markdown, the pages it cites in order of appearance, the searches it ran, and whether it declined.
Cost: charged credits for the data it moves, like every endpoint. Also accepts POST with a JSON body.
Query parameters
Name | Type | Required | Description |
|---|---|---|---|
| string | yes | What to ask, up to 2,000 characters. |
| string | — | Two-letter country the visitor appears to be in. Default |
| string | — | A 5-digit US ZIP code. Results reflect that area (local prices, stock and nearby results). Unknown ZIP codes are a free 400. |
Request
- cURL
- Python
- Node.js
curl -G "https://api.scrapelyserp.com/v1/structured/chatgpt/answer" \
-H "Authorization: Bearer $SCRAPELY_API_KEY" \
--data-urlencode "prompt=best project management tools for small teams" \
-d country=usimport os
import requests
HEADERS = {"Authorization": f"Bearer {os.environ['SCRAPELY_API_KEY']}"}
r = requests.get(
"https://api.scrapelyserp.com/v1/structured/chatgpt/answer",
headers=HEADERS,
params={
"prompt": "best project management tools for small teams",
"country": "us",
},
timeout=30,
)
r.raise_for_status()
data = r.json()const res = await fetch(`https://api.scrapelyserp.com/v1/structured/chatgpt/answer?${new URLSearchParams({
prompt: 'best project management tools for small teams',
country: 'us',
})}`, {
headers: { Authorization: `Bearer ${process.env.SCRAPELY_API_KEY}` },
signal: AbortSignal.timeout(30_000),
});
if (!res.ok) throw new Error((await res.json()).error.code);
const data = await res.json();Response
{
"endpoint": "chatgpt/answer",
"url": "https://chatgpt.com/",
"credits": 0.000028,
"duration_ms": 1480,
"data": {
"prompt": "best project management tools for small teams",
"model": "gpt-6",
"text": "For a small team, shortlist **ClickUp**, **Asana** and **Trello** [1][2].\n\n- **ClickUp:** one workspace for tasks, docs and goals.\n- **Asana:** clear ownership and deadlines.\n- **Trello:** simple boards.",
"citations": [
{
"position": 1,
"url": "https://example.com/best-project-management-software",
"title": "Best project management software for small teams",
"domain": "example.com"
},
{
"position": 2,
"url": "https://example.org/guides/trello-vs-asana",
"title": "Trello vs Asana",
"domain": "example.org"
}
],
"refused": false,
"search_queries": ["best project management software for small teams 2026"]
}
}Responses
Status | Meaning |
|---|---|
| The chatgpt answer as JSON. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Response fields
Field | Type | Description |
|---|---|---|
| string | — |
| string | The page we fetched. |
| number | What the call was charged, in credits, fractional (also |
| integer | — |
| object | An AI assistant's answer to a prompt, as a visitor sees it. |
| string | — |
| string or null | The model that answered, as the assistant names it. |
| string | The answer, markdown. |
| object[] | Sources in the order the assistant lists them, one per URL, each the page itself. |
| boolean | The assistant declined to answer. |
| any | What it searched for, when it says; null when it does not. |
GET /v1/structured/bluesky/search
Bluesky search.
Bluesky posts matching a query, newest or top first: author, text, likes, replies, reposts, tags, links and images. Up to 100 per page; page on with cursor. Sort by latest to watch for mentions.
Cost: charged credits for the data it moves, like every endpoint. Also accepts POST with a JSON body.
Query parameters
Name | Type | Required | Description |
|---|---|---|---|
| string | yes | The search terms. |
| string | — | Result order. One of: |
| integer | — | Posts per page, 1–100. Default |
| string | — | The |
Request
- cURL
- Python
- Node.js
curl -G "https://api.scrapelyserp.com/v1/structured/bluesky/search" \
-H "Authorization: Bearer $SCRAPELY_API_KEY" \
--data-urlencode "query=web scraping" \
-d sort=latestimport os
import requests
HEADERS = {"Authorization": f"Bearer {os.environ['SCRAPELY_API_KEY']}"}
r = requests.get(
"https://api.scrapelyserp.com/v1/structured/bluesky/search",
headers=HEADERS,
params={"query": "web scraping", "sort": "latest"},
timeout=30,
)
r.raise_for_status()
data = r.json()const res = await fetch(`https://api.scrapelyserp.com/v1/structured/bluesky/search?${new URLSearchParams({ query: 'web scraping', sort: 'latest' })}`, {
headers: { Authorization: `Bearer ${process.env.SCRAPELY_API_KEY}` },
signal: AbortSignal.timeout(30_000),
});
if (!res.ok) throw new Error((await res.json()).error.code);
const data = await res.json();Response
{
"endpoint": "bluesky/search",
"url": "https://bsky.app/search?q=web%20scraping",
"credits": 0.000028,
"duration_ms": 1480,
"data": {
"posts": [
{
"id": "at://did:plc:example/app.bsky.feed.post/3mzhmrhqbhq2n",
"url": "https://bsky.app/profile/example.bsky.social/post/3mzhmrhqbhq2n",
"author": "example.bsky.social",
"author_name": "Example",
"title": null,
"text": "Anyone have a web scraping API that handles retries for you?",
"created_at": "2026-11-04T05:30:54.000Z",
"likes": 4,
"replies": 2,
"reposts": 0,
"tags": ["webscraping"],
"community": null,
"link": null,
"media": []
}
],
"next_cursor": "eyJzIjpbMTc5Mzc1OTExNjAwMF19"
}
}Responses
Status | Meaning |
|---|---|
| The bluesky search as JSON. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Response fields
Field | Type | Description |
|---|---|---|
| string | — |
| string | The page we fetched. |
| number | What the call was charged, in credits, fractional (also |
| integer | — |
| object | — |
| object[] | — |
| string or null | Pass as |
GET /v1/structured/bluesky/posts
Bluesky posts.
An account's posts, newest first (its reposts of others left out): text, likes, replies, reposts, tags, links and images. Up to 100 per page; page on with cursor.
Cost: charged credits for the data it moves, like every endpoint. Also accepts POST with a JSON body.
Query parameters
Name | Type | Required | Description |
|---|---|---|---|
| string | yes | The account, e.g. |
| integer | — | Posts per page, 1–100. Default |
| string | — | The |
Request
- cURL
- Python
- Node.js
curl -G "https://api.scrapelyserp.com/v1/structured/bluesky/posts" \
-H "Authorization: Bearer $SCRAPELY_API_KEY" \
-d handle=bsky.appimport os
import requests
HEADERS = {"Authorization": f"Bearer {os.environ['SCRAPELY_API_KEY']}"}
r = requests.get(
"https://api.scrapelyserp.com/v1/structured/bluesky/posts",
headers=HEADERS,
params={"handle": "bsky.app"},
timeout=30,
)
r.raise_for_status()
data = r.json()const res = await fetch(`https://api.scrapelyserp.com/v1/structured/bluesky/posts?${new URLSearchParams({ handle: 'bsky.app' })}`, {
headers: { Authorization: `Bearer ${process.env.SCRAPELY_API_KEY}` },
signal: AbortSignal.timeout(30_000),
});
if (!res.ok) throw new Error((await res.json()).error.code);
const data = await res.json();Response
{
"endpoint": "bluesky/posts",
"url": "https://bsky.app/profile/bsky.app",
"credits": 0.000028,
"duration_ms": 1480,
"data": {
"posts": [
{
"id": "at://did:plc:example/app.bsky.feed.post/3mzhhvhjfwk22",
"url": "https://bsky.app/profile/bsky.app/post/3mzhhvhjfwk22",
"author": "bsky.app",
"author_name": "Bluesky",
"title": null,
"text": "New this week: …",
"created_at": "2026-11-03T18:02:11.000Z",
"likes": 5210,
"replies": 340,
"reposts": 610,
"tags": [],
"community": null,
"link": "https://bsky.social/about/blog",
"media": []
}
],
"next_cursor": "2026-11-03T18:02:11.000Z"
}
}Responses
Status | Meaning |
|---|---|
| The bluesky posts as JSON. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Response fields
Field | Type | Description |
|---|---|---|
| string | — |
| string | The page we fetched. |
| number | What the call was charged, in credits, fractional (also |
| integer | — |
| object | — |
| object[] | — |
| string or null | Pass as |
GET /v1/structured/mastodon/hashtag
Mastodon hashtag.
Public posts with a hashtag on a Mastodon server (mastodon.social unless you name another), newest first: author, text, likes, replies, boosts, tags, links and media. Up to 40 per page; page on with cursor.
Cost: charged credits for the data it moves, like every endpoint. Also accepts POST with a JSON body.
Query parameters
Name | Type | Required | Description |
|---|---|---|---|
| string | yes | The hashtag, with or without |
| string | — | The Mastodon server to read, e.g. |
| integer | — | Posts per page, 1–40. Default |
| string | — | The |
Request
- cURL
- Python
- Node.js
curl -G "https://api.scrapelyserp.com/v1/structured/mastodon/hashtag" \
-H "Authorization: Bearer $SCRAPELY_API_KEY" \
-d tag=webscrapingimport os
import requests
HEADERS = {"Authorization": f"Bearer {os.environ['SCRAPELY_API_KEY']}"}
r = requests.get(
"https://api.scrapelyserp.com/v1/structured/mastodon/hashtag",
headers=HEADERS,
params={"tag": "webscraping"},
timeout=30,
)
r.raise_for_status()
data = r.json()const res = await fetch(`https://api.scrapelyserp.com/v1/structured/mastodon/hashtag?${new URLSearchParams({ tag: 'webscraping' })}`, {
headers: { Authorization: `Bearer ${process.env.SCRAPELY_API_KEY}` },
signal: AbortSignal.timeout(30_000),
});
if (!res.ok) throw new Error((await res.json()).error.code);
const data = await res.json();Response
{
"endpoint": "mastodon/hashtag",
"url": "https://mastodon.social/tags/webscraping",
"credits": 0.000028,
"duration_ms": 1480,
"data": {
"posts": [
{
"id": "117553103172691630",
"url": "https://mastodon.social/@example/117553103172691630",
"author": "example@mastodon.social",
"author_name": "Example",
"title": null,
"text": "Pages come back blank behind an authenticated proxy? Here is what fixed it for us.",
"created_at": "2026-11-03T15:00:04.979Z",
"likes": 3,
"replies": 0,
"reposts": 1,
"tags": ["webscraping", "python"],
"community": null,
"link": null,
"media": []
}
],
"next_cursor": "117553103172691630"
}
}Responses
Status | Meaning |
|---|---|
| The mastodon hashtag as JSON. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Response fields
Field | Type | Description |
|---|---|---|
| string | — |
| string | The page we fetched. |
| number | What the call was charged, in credits, fractional (also |
| integer | — |
| object | — |
| object[] | — |
| string or null | Pass as |
GET /v1/structured/lemmy/search
Lemmy search.
Lemmy posts matching a query on a Lemmy server (lemmy.world unless you name another): title, author, community, score, comment count, text and link. Up to 50 per page; page on with cursor.
Cost: charged credits for the data it moves, like every endpoint. Also accepts POST with a JSON body.
Query parameters
Name | Type | Required | Description |
|---|---|---|---|
| string | yes | The search terms. |
| string | — | The Lemmy server to read, e.g. |
| string | — | Result order. One of: |
| integer | — | Posts per page, 1–50. Default |
| string | — | The |
Request
- cURL
- Python
- Node.js
curl -G "https://api.scrapelyserp.com/v1/structured/lemmy/search" \
-H "Authorization: Bearer $SCRAPELY_API_KEY" \
--data-urlencode "query=web scraping"import os
import requests
HEADERS = {"Authorization": f"Bearer {os.environ['SCRAPELY_API_KEY']}"}
r = requests.get(
"https://api.scrapelyserp.com/v1/structured/lemmy/search",
headers=HEADERS,
params={"query": "web scraping"},
timeout=30,
)
r.raise_for_status()
data = r.json()const res = await fetch(`https://api.scrapelyserp.com/v1/structured/lemmy/search?${new URLSearchParams({ query: 'web scraping' })}`, {
headers: { Authorization: `Bearer ${process.env.SCRAPELY_API_KEY}` },
signal: AbortSignal.timeout(30_000),
});
if (!res.ok) throw new Error((await res.json()).error.code);
const data = await res.json();Response
{
"endpoint": "lemmy/search",
"url": "https://lemmy.world/search?q=web%20scraping&type=Posts",
"credits": 0.000028,
"duration_ms": 1480,
"data": {
"posts": [
{
"id": "53928115",
"url": "https://lemmy.world/post/53928115",
"author": "example@lemmy.world",
"author_name": null,
"title": "Outlets sue a browser maker over alleged content scraping",
"text": "The suit says the content was redistributed to customers…",
"created_at": "2026-11-03T05:29:22.928Z",
"likes": 218,
"replies": 45,
"reposts": null,
"tags": [],
"community": "technology@lemmy.world",
"link": "https://example.com/news/scraping-lawsuit",
"media": []
}
],
"next_cursor": "2"
}
}Responses
Status | Meaning |
|---|---|
| The lemmy search as JSON. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Response fields
Field | Type | Description |
|---|---|---|
| string | — |
| string | The page we fetched. |
| number | What the call was charged, in credits, fractional (also |
| integer | — |
| object | — |
| object[] | — |
| string or null | Pass as |
GET /v1/structured/lemmy/posts
Lemmy community posts.
A Lemmy community's posts, newest (or hot, top…) first: title, author, score, comment count, text and link. Up to 50 per page; page on with cursor.
Cost: charged credits for the data it moves, like every endpoint. Also accepts POST with a JSON body.
Query parameters
Name | Type | Required | Description |
|---|---|---|---|
| string | yes | The community, e.g. |
| string | — | The Lemmy server to read, e.g. |
| string | — | Result order. One of: |
| integer | — | Posts per page, 1–50. Default |
| string | — | The |
Request
- cURL
- Python
- Node.js
curl -G "https://api.scrapelyserp.com/v1/structured/lemmy/posts" \
-H "Authorization: Bearer $SCRAPELY_API_KEY" \
-d community=technologyimport os
import requests
HEADERS = {"Authorization": f"Bearer {os.environ['SCRAPELY_API_KEY']}"}
r = requests.get(
"https://api.scrapelyserp.com/v1/structured/lemmy/posts",
headers=HEADERS,
params={"community": "technology"},
timeout=30,
)
r.raise_for_status()
data = r.json()const res = await fetch(`https://api.scrapelyserp.com/v1/structured/lemmy/posts?${new URLSearchParams({ community: 'technology' })}`, {
headers: { Authorization: `Bearer ${process.env.SCRAPELY_API_KEY}` },
signal: AbortSignal.timeout(30_000),
});
if (!res.ok) throw new Error((await res.json()).error.code);
const data = await res.json();Response
{
"endpoint": "lemmy/posts",
"url": "https://lemmy.world/c/technology",
"credits": 0.000028,
"duration_ms": 1480,
"data": {
"posts": [
{
"id": "53928115",
"url": "https://lemmy.world/post/53928115",
"author": "example@lemmy.world",
"author_name": null,
"title": "Outlets sue a browser maker over alleged content scraping",
"text": "",
"created_at": "2026-11-03T05:29:22.928Z",
"likes": 218,
"replies": 45,
"reposts": null,
"tags": [],
"community": "technology@lemmy.world",
"link": "https://example.com/news/scraping-lawsuit",
"media": []
}
],
"next_cursor": "2"
}
}Responses
Status | Meaning |
|---|---|
| The lemmy community posts as JSON. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Response fields
Field | Type | Description |
|---|---|---|
| string | — |
| string | The page we fetched. |
| number | What the call was charged, in credits, fractional (also |
| integer | — |
| object | — |
| object[] | — |
| string or null | Pass as |
GET /v1/structured/discourse/search
Discourse search.
Posts matching a query on any public Discourse forum: topic title, author, excerpt, likes, tags and category. 50 per page; page on with cursor.
Cost: charged credits for the data it moves, like every endpoint. Also accepts POST with a JSON body.
Query parameters
Name | Type | Required | Description |
|---|---|---|---|
| string | yes | The forum’s domain, e.g. |
| string | yes | The search terms. |
| string | — | The |
Request
- cURL
- Python
- Node.js
curl -G "https://api.scrapelyserp.com/v1/structured/discourse/search" \
-H "Authorization: Bearer $SCRAPELY_API_KEY" \
-d forum=meta.discourse.org \
-d query=webhooksimport os
import requests
HEADERS = {"Authorization": f"Bearer {os.environ['SCRAPELY_API_KEY']}"}
r = requests.get(
"https://api.scrapelyserp.com/v1/structured/discourse/search",
headers=HEADERS,
params={"forum": "meta.discourse.org", "query": "webhooks"},
timeout=30,
)
r.raise_for_status()
data = r.json()const res = await fetch(`https://api.scrapelyserp.com/v1/structured/discourse/search?${new URLSearchParams({ forum: 'meta.discourse.org', query: 'webhooks' })}`, {
headers: { Authorization: `Bearer ${process.env.SCRAPELY_API_KEY}` },
signal: AbortSignal.timeout(30_000),
});
if (!res.ok) throw new Error((await res.json()).error.code);
const data = await res.json();Response
{
"endpoint": "discourse/search",
"url": "https://meta.discourse.org/search?q=webhooks",
"credits": 0.000028,
"duration_ms": 1480,
"data": {
"posts": [
{
"id": "257177",
"url": "https://meta.discourse.org/t/trigger-a-zapier-task-with-discourse-webhooks/56753/1",
"author": "example",
"author_name": null,
"title": "Trigger a Zapier task with Discourse Webhooks",
"text": "Want to use Discourse Webhooks to trigger a task? Let's get started!…",
"created_at": "2017-02-01T22:11:22.521Z",
"likes": 32,
"replies": 29,
"reposts": null,
"tags": ["webhooks"],
"community": "Documentation",
"link": null,
"media": []
}
],
"next_cursor": "2"
}
}Responses
Status | Meaning |
|---|---|
| The discourse search as JSON. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Response fields
Field | Type | Description |
|---|---|---|
| string | — |
| string | The page we fetched. |
| number | What the call was charged, in credits, fractional (also |
| integer | — |
| object | — |
| object[] | — |
| string or null | Pass as |
GET /v1/structured/discourse/topic
Discourse topic.
A topic's posts in order on any public Discourse forum, the opening post first: author, text, likes and replies. 20 per page; page on with cursor.
Cost: charged credits for the data it moves, like every endpoint. Also accepts POST with a JSON body.
Query parameters
Name | Type | Required | Description |
|---|---|---|---|
| string | yes | The forum’s domain, e.g. |
| string | yes | The topic’s id, e.g. |
| string | — | The |
Request
- cURL
- Python
- Node.js
curl -G "https://api.scrapelyserp.com/v1/structured/discourse/topic" \
-H "Authorization: Bearer $SCRAPELY_API_KEY" \
-d forum=meta.discourse.org \
-d topic=49045import os
import requests
HEADERS = {"Authorization": f"Bearer {os.environ['SCRAPELY_API_KEY']}"}
r = requests.get(
"https://api.scrapelyserp.com/v1/structured/discourse/topic",
headers=HEADERS,
params={"forum": "meta.discourse.org", "topic": "49045"},
timeout=30,
)
r.raise_for_status()
data = r.json()const res = await fetch(`https://api.scrapelyserp.com/v1/structured/discourse/topic?${new URLSearchParams({ forum: 'meta.discourse.org', topic: '49045' })}`, {
headers: { Authorization: `Bearer ${process.env.SCRAPELY_API_KEY}` },
signal: AbortSignal.timeout(30_000),
});
if (!res.ok) throw new Error((await res.json()).error.code);
const data = await res.json();Response
{
"endpoint": "discourse/topic",
"url": "https://meta.discourse.org/t/49045",
"credits": 0.000028,
"duration_ms": 1480,
"data": {
"posts": [
{
"id": "217834",
"url": "https://meta.discourse.org/t/setting-up-webhooks/49045/1",
"author": "example",
"author_name": "Example",
"title": "Setting up webhooks",
"text": "A webhook is a way to notify external services about changes…",
"created_at": "2016-08-23T05:36:09.935Z",
"likes": 74,
"replies": 3,
"reposts": null,
"tags": ["howto"],
"community": null,
"link": null,
"media": []
}
],
"next_cursor": null
}
}Responses
Status | Meaning |
|---|---|
| The discourse topic as JSON. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Response fields
Field | Type | Description |
|---|---|---|
| string | — |
| string | The page we fetched. |
| number | What the call was charged, in credits, fractional (also |
| integer | — |
| object | — |
| object[] | — |
| string or null | Pass as |
GET /v1/structured/tumblr/tagged
Tumblr tagged posts.
Tumblr posts with a tag, newest or top first: blog, text, notes, reblogs, tags, links and images. Up to 20 per page; page on with cursor.
Cost: charged credits for the data it moves, like every endpoint. Also accepts POST with a JSON body.
Query parameters
Name | Type | Required | Description |
|---|---|---|---|
| string | yes | The tag, e.g. |
| string | — | Result order. One of: |
| integer | — | Posts per page, 1–20. Default |
| string | — | The |
Request
- cURL
- Python
- Node.js
curl -G "https://api.scrapelyserp.com/v1/structured/tumblr/tagged" \
-H "Authorization: Bearer $SCRAPELY_API_KEY" \
-d tag=webscrapingimport os
import requests
HEADERS = {"Authorization": f"Bearer {os.environ['SCRAPELY_API_KEY']}"}
r = requests.get(
"https://api.scrapelyserp.com/v1/structured/tumblr/tagged",
headers=HEADERS,
params={"tag": "webscraping"},
timeout=30,
)
r.raise_for_status()
data = r.json()const res = await fetch(`https://api.scrapelyserp.com/v1/structured/tumblr/tagged?${new URLSearchParams({ tag: 'webscraping' })}`, {
headers: { Authorization: `Bearer ${process.env.SCRAPELY_API_KEY}` },
signal: AbortSignal.timeout(30_000),
});
if (!res.ok) throw new Error((await res.json()).error.code);
const data = await res.json();Response
{
"endpoint": "tumblr/tagged",
"url": "https://www.tumblr.com/tagged/webscraping?sort=recent",
"credits": 0.000028,
"duration_ms": 1480,
"data": {
"posts": [
{
"id": "832269873172381696",
"url": "https://www.tumblr.com/example/832269873172381696/selenium-will-tell-you-everything-is-fine",
"author": "example",
"author_name": null,
"title": null,
"text": "selenium will tell you everything is fine. no exception. no error…",
"created_at": "2026-11-03T15:00:02.000Z",
"likes": 6,
"replies": 0,
"reposts": 2,
"tags": ["webscraping", "python"],
"community": null,
"link": null,
"media": []
}
],
"next_cursor": "VGh4j_eC7tsoXm676sVywGIYynqF"
}
}Responses
Status | Meaning |
|---|---|
| The tumblr tagged posts as JSON. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Response fields
Field | Type | Description |
|---|---|---|
| string | — |
| string | The page we fetched. |
| number | What the call was charged, in credits, fractional (also |
| integer | — |
| object | — |
| object[] | — |
| string or null | Pass as |
GET /v1/structured/tumblr/search
Tumblr search.
Tumblr posts matching a query, newest or top first: blog, text, notes, reblogs, tags, links and images. Up to 20 per page; page on with cursor.
Cost: charged credits for the data it moves, like every endpoint. Also accepts POST with a JSON body.
Query parameters
Name | Type | Required | Description |
|---|---|---|---|
| string | yes | The search terms. |
| string | — | Result order. One of: |
| integer | — | Posts per page, 1–20. Default |
| string | — | The |
Request
- cURL
- Python
- Node.js
curl -G "https://api.scrapelyserp.com/v1/structured/tumblr/search" \
-H "Authorization: Bearer $SCRAPELY_API_KEY" \
--data-urlencode "query=web scraping"import os
import requests
HEADERS = {"Authorization": f"Bearer {os.environ['SCRAPELY_API_KEY']}"}
r = requests.get(
"https://api.scrapelyserp.com/v1/structured/tumblr/search",
headers=HEADERS,
params={"query": "web scraping"},
timeout=30,
)
r.raise_for_status()
data = r.json()const res = await fetch(`https://api.scrapelyserp.com/v1/structured/tumblr/search?${new URLSearchParams({ query: 'web scraping' })}`, {
headers: { Authorization: `Bearer ${process.env.SCRAPELY_API_KEY}` },
signal: AbortSignal.timeout(30_000),
});
if (!res.ok) throw new Error((await res.json()).error.code);
const data = await res.json();Response
{
"endpoint": "tumblr/search",
"url": "https://www.tumblr.com/search/web%20scraping",
"credits": 0.000028,
"duration_ms": 1480,
"data": {
"posts": [
{
"id": "832269873172381696",
"url": "https://www.tumblr.com/example/832269873172381696/selenium-will-tell-you-everything-is-fine",
"author": "example",
"author_name": null,
"title": null,
"text": "selenium will tell you everything is fine. no exception. no error…",
"created_at": "2026-11-03T15:00:02.000Z",
"likes": 6,
"replies": 0,
"reposts": 2,
"tags": ["selenium", "web scraping"],
"community": null,
"link": null,
"media": []
}
],
"next_cursor": "VGh4j_eC7tsoXm676sVywGIYynqF"
}
}Responses
Status | Meaning |
|---|---|
| The tumblr search as JSON. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Response fields
Field | Type | Description |
|---|---|---|
| string | — |
| string | The page we fetched. |
| number | What the call was charged, in credits, fractional (also |
| integer | — |
| object | — |
| object[] | — |
| string or null | Pass as |
GET /v1/structured/hackernews/search
Hacker News search.
Hacker News stories and comments matching a query, by relevance or newest first, with filters for type, author, story, points and dates: title, link, author, points, comment count, text and time. Up to 100 per page; page on with cursor. A query reaches only its first 1,000 results.
Cost: charged credits for the data it moves, like every endpoint. Also accepts POST with a JSON body.
Query parameters
Name | Type | Required | Description |
|---|---|---|---|
| string | — | The search terms. Leave it out to list by the filters alone. |
| string | — |
|
| string | — |
|
| string | — | Only this user’s items, e.g. |
| string | — | Only items under this story (its id or link): with |
| integer | — | Only items with at least this many points. Comments have no points. |
| string | — | Only items from this time on: |
| string | — | Only items up to this time, in the same forms. |
| integer | — | Results per page, 1–100. Default |
| string | — | The |
Request
- cURL
- Python
- Node.js
curl -G "https://api.scrapelyserp.com/v1/structured/hackernews/search" \
-H "Authorization: Bearer $SCRAPELY_API_KEY" \
--data-urlencode "query=web scraping" \
-d sort=dateimport os
import requests
HEADERS = {"Authorization": f"Bearer {os.environ['SCRAPELY_API_KEY']}"}
r = requests.get(
"https://api.scrapelyserp.com/v1/structured/hackernews/search",
headers=HEADERS,
params={"query": "web scraping", "sort": "date"},
timeout=30,
)
r.raise_for_status()
data = r.json()const res = await fetch(`https://api.scrapelyserp.com/v1/structured/hackernews/search?${new URLSearchParams({ query: 'web scraping', sort: 'date' })}`, {
headers: { Authorization: `Bearer ${process.env.SCRAPELY_API_KEY}` },
signal: AbortSignal.timeout(30_000),
});
if (!res.ok) throw new Error((await res.json()).error.code);
const data = await res.json();Response
{
"endpoint": "hackernews/search",
"url": "https://news.ycombinator.com/",
"credits": 0.000028,
"duration_ms": 1480,
"data": {
"query": "rust",
"type": "story",
"sort": "relevance",
"total": 61311,
"results": [
{
"id": "22238335",
"type": "story",
"title": "Why Discord is switching from Go to Rust",
"url": "https://blog.discordapp.com/why-discord-is-switching-from-go-to-rust-a190bbca2b1f",
"hn_url": "https://news.ycombinator.com/item?id=22238335",
"author": "Sikul",
"points": 1582,
"comments": 642,
"text": null,
"created_at": "2020-02-04T17:30:40.000Z",
"story_id": "22238335",
"story_title": null,
"parent_id": null
}
],
"next_cursor": "2"
}
}Responses
Status | Meaning |
|---|---|
| The hacker news search as JSON. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Response fields
Field | Type | Description |
|---|---|---|
| string | — |
| string | The page we fetched. |
| number | What the call was charged, in credits, fractional (also |
| integer | — |
| object | — |
| string or null | — |
| string | — |
| string | — |
| integer | Every match. Only the first 1,000 can be paged to. |
| object[] | — |
| string or null | Pass as |
GET /v1/structured/hackernews/items
Hacker News items.
Up to 100 Hacker News items by id, in one call: title, link, author, points, comment count, text, time and the ids of its replies. Ids that are not items come back in not_found.
Cost: charged credits for the data it moves, like every endpoint. Also accepts POST with a JSON body.
Query parameters
Name | Type | Required | Description |
|---|---|---|---|
| string | yes | Comma-separated item ids (stories, comments, jobs, polls), e.g. |
Request
- cURL
- Python
- Node.js
curl -G "https://api.scrapelyserp.com/v1/structured/hackernews/items" \
-H "Authorization: Bearer $SCRAPELY_API_KEY" \
--data-urlencode "ids=8863,2921983"import os
import requests
HEADERS = {"Authorization": f"Bearer {os.environ['SCRAPELY_API_KEY']}"}
r = requests.get(
"https://api.scrapelyserp.com/v1/structured/hackernews/items",
headers=HEADERS,
params={"ids": "8863,2921983"},
timeout=30,
)
r.raise_for_status()
data = r.json()const res = await fetch(`https://api.scrapelyserp.com/v1/structured/hackernews/items?${new URLSearchParams({ ids: '8863,2921983' })}`, {
headers: { Authorization: `Bearer ${process.env.SCRAPELY_API_KEY}` },
signal: AbortSignal.timeout(30_000),
});
if (!res.ok) throw new Error((await res.json()).error.code);
const data = await res.json();Response
{
"endpoint": "hackernews/items",
"url": "https://news.ycombinator.com/item?id=8863",
"credits": 0.000028,
"duration_ms": 1480,
"data": {
"items": [
{
"id": "8863",
"type": "story",
"title": "My YC app: Dropbox - Throw away your USB drive",
"url": "http://www.getdropbox.com/u/2/screencast.html",
"hn_url": "https://news.ycombinator.com/item?id=8863",
"author": "dhouston",
"points": 104,
"comments": 71,
"text": null,
"created_at": "2007-04-04T19:16:40.000Z",
"parent_id": null,
"reply_ids": ["9224", "8917", "8884"],
"deleted": false,
"dead": false
}
],
"not_found": []
}
}Responses
Status | Meaning |
|---|---|
| The hacker news items as JSON. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Response fields
Field | Type | Description |
|---|---|---|
| string | — |
| string | The page we fetched. |
| number | What the call was charged, in credits, fractional (also |
| integer | — |
| object | — |
| object[] | In the order asked. |
| string[] | Asked-for ids that are not items. |
GET /v1/structured/devto/articles
Dev.to articles.
Dev.to articles, newest first, or the most popular of the last days: title, summary, author, tags, reactions, comment count, reading time and link. Narrow by tag or username. Dev.to has no keyword search, so there is none here. Up to 100 per page; page on with cursor.
Cost: charged credits for the data it moves, like every endpoint. Also accepts POST with a JSON body.
Query parameters
Name | Type | Required | Description |
|---|---|---|---|
| string | — | Only articles with this tag, e.g. |
| string | — | Only this author’s articles, e.g. |
| integer | — | The most-reacted articles of the last N days (1–366) instead of the newest. |
| string | — |
|
| integer | — | Articles per page, 1–100. Default |
| string | — | The |
Request
- cURL
- Python
- Node.js
curl -G "https://api.scrapelyserp.com/v1/structured/devto/articles" \
-H "Authorization: Bearer $SCRAPELY_API_KEY" \
-d tag=webdev \
-d top=7import os
import requests
HEADERS = {"Authorization": f"Bearer {os.environ['SCRAPELY_API_KEY']}"}
r = requests.get(
"https://api.scrapelyserp.com/v1/structured/devto/articles",
headers=HEADERS,
params={"tag": "webdev", "top": "7"},
timeout=30,
)
r.raise_for_status()
data = r.json()const res = await fetch(`https://api.scrapelyserp.com/v1/structured/devto/articles?${new URLSearchParams({ tag: 'webdev', top: '7' })}`, {
headers: { Authorization: `Bearer ${process.env.SCRAPELY_API_KEY}` },
signal: AbortSignal.timeout(30_000),
});
if (!res.ok) throw new Error((await res.json()).error.code);
const data = await res.json();Response
{
"endpoint": "devto/articles",
"url": "https://dev.to/t/webdev",
"credits": 0.000028,
"duration_ms": 1480,
"data": {
"tag": "webdev",
"username": null,
"top": 7,
"state": null,
"articles": [
{
"id": "4943245",
"title": "Yes, {} + [] = 0. No, JavaScript Isn’t Broken",
"description": "JavaScript is a legendary programming language. Created in the mid-1990s in just 10 days, it was born...",
"url": "https://dev.to/sylwia-lask/yes-0-no-javascript-isnt-broken-14ob",
"author": "sylwia-lask",
"author_name": "Sylwia Laskowska",
"organization": null,
"published_at": "2026-10-29T20:08:53Z",
"edited_at": "2026-10-30T13:34:02Z",
"tags": ["javascript", "webdev", "node", "programming"],
"reactions": 170,
"comments": 126,
"reading_time_minutes": 5,
"language": "en",
"cover_image": null,
"canonical_url": "https://dev.to/sylwia-lask/yes-0-no-javascript-isnt-broken-14ob"
}
],
"next_cursor": "2"
}
}Responses
Status | Meaning |
|---|---|
| The dev.to articles as JSON. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Response fields
Field | Type | Description |
|---|---|---|
| string | — |
| string | The page we fetched. |
| number | What the call was charged, in credits, fractional (also |
| integer | — |
| object | — |
| string or null | — |
| string or null | — |
| integer or null | — |
| string or null | — |
| object[] | — |
| string or null | Pass as |
GET /v1/structured/devto/article
Dev.to article.
One Dev.to article in full: the Markdown body with its title, author, tags, reactions, comment count and dates.
Cost: charged credits for the data it moves, like every endpoint. Also accepts POST with a JSON body.
Query parameters
Name | Type | Required | Description |
|---|---|---|---|
| string | yes | The article’s id (e.g. |
Request
- cURL
- Python
- Node.js
curl -G "https://api.scrapelyserp.com/v1/structured/devto/article" \
-H "Authorization: Bearer $SCRAPELY_API_KEY" \
-d article=4943245import os
import requests
HEADERS = {"Authorization": f"Bearer {os.environ['SCRAPELY_API_KEY']}"}
r = requests.get(
"https://api.scrapelyserp.com/v1/structured/devto/article",
headers=HEADERS,
params={"article": "4943245"},
timeout=30,
)
r.raise_for_status()
data = r.json()const res = await fetch(`https://api.scrapelyserp.com/v1/structured/devto/article?${new URLSearchParams({ article: '4943245' })}`, {
headers: { Authorization: `Bearer ${process.env.SCRAPELY_API_KEY}` },
signal: AbortSignal.timeout(30_000),
});
if (!res.ok) throw new Error((await res.json()).error.code);
const data = await res.json();Response
{
"endpoint": "devto/article",
"url": "https://dev.to/sylwia-lask/yes-0-no-javascript-isnt-broken-14ob",
"credits": 0.000028,
"duration_ms": 1480,
"data": {
"id": "4943245",
"title": "Yes, {} + [] = 0. No, JavaScript Isn’t Broken",
"description": "JavaScript is a legendary programming language. Created in the mid-1990s in just 10 days, it was born...",
"url": "https://dev.to/sylwia-lask/yes-0-no-javascript-isnt-broken-14ob",
"author": "sylwia-lask",
"author_name": "Sylwia Laskowska",
"organization": null,
"published_at": "2026-10-29T20:08:53Z",
"edited_at": "2026-10-30T13:34:02Z",
"tags": ["javascript", "webdev", "node", "programming"],
"reactions": 170,
"comments": 126,
"reading_time_minutes": 5,
"language": "en",
"cover_image": null,
"canonical_url": "https://dev.to/sylwia-lask/yes-0-no-javascript-isnt-broken-14ob",
"body_markdown": "JavaScript is a legendary programming language…"
}
}Responses
Status | Meaning |
|---|---|
| The dev.to article as JSON. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Response fields
Field | Type | Description |
|---|---|---|
| string | — |
| string | The page we fetched. |
| number | What the call was charged, in credits, fractional (also |
| integer | — |
| object | — |
| string | Dev.to's article id. |
| string | — |
| string or null | The summary Dev.to shows under the title. |
| string | — |
| string or null | Username. |
| string or null | — |
| string or null | The organisation it was published under, if any. |
| string (date-time) or null | — |
| string (date-time) or null | — |
| string[] | — |
| integer or null | Public reactions, all kinds. |
| integer or null | — |
| integer or null | — |
| string or null | — |
| string or null | — |
| string or null | Where the article was first published, when crossposted. |
| string | The article as Markdown. |
GET /v1/structured/wikipedia/search
Wikipedia search.
Wikipedia pages matching a query, best match first: title, page id, link, a snippet with the match, word count and last edit. Up to 50 per page; page on with cursor.
Cost: charged credits for the data it moves, like every endpoint. Also accepts POST with a JSON body.
Query parameters
Name | Type | Required | Description |
|---|---|---|---|
| string | yes | The search terms. |
| string | — | The language edition. One of: |
| integer | — | Results per page, 1–50. Default |
| string | — | The |
Request
- cURL
- Python
- Node.js
curl -G "https://api.scrapelyserp.com/v1/structured/wikipedia/search" \
-H "Authorization: Bearer $SCRAPELY_API_KEY" \
--data-urlencode "query=web scraping"import os
import requests
HEADERS = {"Authorization": f"Bearer {os.environ['SCRAPELY_API_KEY']}"}
r = requests.get(
"https://api.scrapelyserp.com/v1/structured/wikipedia/search",
headers=HEADERS,
params={"query": "web scraping"},
timeout=30,
)
r.raise_for_status()
data = r.json()const res = await fetch(`https://api.scrapelyserp.com/v1/structured/wikipedia/search?${new URLSearchParams({ query: 'web scraping' })}`, {
headers: { Authorization: `Bearer ${process.env.SCRAPELY_API_KEY}` },
signal: AbortSignal.timeout(30_000),
});
if (!res.ok) throw new Error((await res.json()).error.code);
const data = await res.json();Response
{
"endpoint": "wikipedia/search",
"url": "https://en.wikipedia.org/",
"credits": 0.000028,
"duration_ms": 1480,
"data": {
"query": "web scraping",
"lang": "en",
"total": 2117,
"results": [
{
"title": "Web scraping",
"page_id": 2696619,
"url": "https://en.wikipedia.org/wiki/Web_scraping",
"snippet": "Web scraping, web harvesting, or web data extraction is data scraping used for extracting data from websites. Web scraping software may directly access",
"word_count": 4123,
"last_modified": "2026-11-01T23:24:25Z"
}
],
"next_cursor": "50"
}
}Responses
Status | Meaning |
|---|---|
| The wikipedia search as JSON. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Response fields
Field | Type | Description |
|---|---|---|
| string | — |
| string | The page we fetched. |
| number | What the call was charged, in credits, fractional (also |
| integer | — |
| object | — |
| string | — |
| string | — |
| integer | Every match; 0 for a query that matches nothing. |
| object[] | — |
| string or null | Pass as |
GET /v1/structured/wikipedia/pages
Wikipedia pages.
Up to 100 Wikipedia pages by title, in one call: the opening text, the short description, the Wikidata id, whether it is a disambiguation page, and the last edit. Redirects are followed and reported. Titles that are not pages come back in not_found.
Cost: charged credits for the data it moves, like every endpoint. Also accepts POST with a JSON body.
Query parameters
Name | Type | Required | Description |
|---|---|---|---|
| string | yes | Page titles separated by |
| string | — | The language edition. One of: |
Request
- cURL
- Python
- Node.js
curl -G "https://api.scrapelyserp.com/v1/structured/wikipedia/pages" \
-H "Authorization: Bearer $SCRAPELY_API_KEY" \
--data-urlencode "titles=Albert Einstein|Web scraping"import os
import requests
HEADERS = {"Authorization": f"Bearer {os.environ['SCRAPELY_API_KEY']}"}
r = requests.get(
"https://api.scrapelyserp.com/v1/structured/wikipedia/pages",
headers=HEADERS,
params={"titles": "Albert Einstein|Web scraping"},
timeout=30,
)
r.raise_for_status()
data = r.json()const res = await fetch(`https://api.scrapelyserp.com/v1/structured/wikipedia/pages?${new URLSearchParams({ titles: 'Albert Einstein|Web scraping' })}`, {
headers: { Authorization: `Bearer ${process.env.SCRAPELY_API_KEY}` },
signal: AbortSignal.timeout(30_000),
});
if (!res.ok) throw new Error((await res.json()).error.code);
const data = await res.json();Response
{
"endpoint": "wikipedia/pages",
"url": "https://en.wikipedia.org/wiki/Mercury",
"credits": 0.000028,
"duration_ms": 1480,
"data": {
"lang": "en",
"pages": [
{
"requested": "Mercury",
"title": "Mercury",
"page_id": 19007,
"url": "https://en.wikipedia.org/wiki/Mercury",
"redirected_from": null,
"description": "Topics referred to by the same term",
"extract": "Mercury most commonly refers to:\n\nMercury (planet), the closest planet to the Sun",
"wikidata_id": "Q48397",
"disambiguation": true,
"last_modified": "2026-11-02T03:24:00Z",
"length": 8268
}
],
"not_found": []
}
}Responses
Status | Meaning |
|---|---|
| The wikipedia pages as JSON. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Response fields
Field | Type | Description |
|---|---|---|
| string | — |
| string | The page we fetched. |
| number | What the call was charged, in credits, fractional (also |
| integer | — |
| object | — |
| string | — |
| object[] | In the order asked; two titles that redirect to one page give two entries. |
| string[] | Asked-for titles that are not pages. |
GET /v1/structured/reverb/search
Reverb search.
Reverb listings for a query, a category, or both, in the site's order: listing id, title, make (as brand), listed price, price before a price drop, condition, model, year, finish, category, shop name, offers flag, quantity, shipping price, dates, link and image. 50 per page; send next_page back as page for more. sort is best_match, newest, price_low or price_high; status is available (the default) or sold; condition, min_price / max_price (whole dollars) and category (a category slug such as electric-guitars) narrow the listings. Only the first 2,500 listings of a query can be reached (page 1 to 50), however large total_results says: narrow the query to see more. price is the listed price, on a sold listing too: it is the last price the listing carried, not necessarily what the buyer paid, and Reverb gives no sold price and no sold date. list_price is the price before a price drop, null when the price never dropped. rating and reviews_count are always null: listings have none (the shop's rating is on a product lookup, not in search). in_stock is true for an available listing and false for a sold one. Each id is what POST /v1/products takes for Reverb. A keyword that matches nothing is an empty list (total_results 0); an answer that is not clearly a list of listings in the state asked for, or a total of zero, is a failure (502 TARGET_FAILED, not charged, retry it), not an empty list. A request with neither query nor category is refused.
Cost: charged credits for the data it moves, like every endpoint. Also accepts POST with a JSON body.
Query parameters
Name | Type | Required | Description |
|---|---|---|---|
| string | — | The search terms. Required unless |
| string | — | A Reverb category slug ( |
| integer | — | Results page, 1–50 (50 listings each: the first 2,500 of a query). Default |
| string | — | Result order: |
| string | — | Listings for sale, or sold ones (their last listed price; Reverb gives no sold price or date). One of: |
| string | — | Only listings in this condition. One of: |
| string | — | Lowest listed price, in whole dollars. |
| string | — | Highest listed price, in whole dollars. |
Request
- cURL
- Python
- Node.js
curl -G "https://api.scrapelyserp.com/v1/structured/reverb/search" \
-H "Authorization: Bearer $SCRAPELY_API_KEY" \
--data-urlencode "query=boss ds-1"import os
import requests
HEADERS = {"Authorization": f"Bearer {os.environ['SCRAPELY_API_KEY']}"}
r = requests.get(
"https://api.scrapelyserp.com/v1/structured/reverb/search",
headers=HEADERS,
params={"query": "boss ds-1"},
timeout=30,
)
r.raise_for_status()
data = r.json()const res = await fetch(`https://api.scrapelyserp.com/v1/structured/reverb/search?${new URLSearchParams({ query: 'boss ds-1' })}`, {
headers: { Authorization: `Bearer ${process.env.SCRAPELY_API_KEY}` },
signal: AbortSignal.timeout(30_000),
});
if (!res.ok) throw new Error((await res.json()).error.code);
const data = await res.json();Response
{
"endpoint": "reverb/search",
"url": "https://reverb.com/marketplace?query=boss%20ds-1",
"credits": 0.000028,
"duration_ms": 1480,
"data": {
"query": "boss ds-1",
"page": 1,
"total_results": 979,
"next_page": 2,
"results": [
{
"position": 1,
"id": "101847259",
"title": "Keeley Boss DS-1 Distortion Mod",
"url": "https://reverb.com/item/101847259-keeley-boss-ds-1-distortion-mod",
"brand": "Keeley",
"image": "https://rvb-img.reverb.com/i/s--aXAmI2rm--/quality=medium-low,height=800,width=800,fit=contain/bd04a110-5204-4f71-84fe-320e0e5ff53a.jpeg",
"price": {
"amount": 125,
"currency": "USD"
},
"list_price": null,
"rating": null,
"reviews_count": null,
"in_stock": true,
"reverb": {
"status": "available",
"condition": "Very Good",
"make": "Keeley",
"model": "Boss DS-1 Distortion with Ultra Mod",
"year": "2010s",
"finish": "Orange",
"category": "Effects and Pedals / Distortion",
"seller": "Telecasters",
"sellerRating": null,
"sellerFeedbackCount": null,
"offersEnabled": true,
"quantity": 1,
"shippingPrice": {
"amount": 11.88,
"currency": "USD"
},
"shippingEstimated": true,
"createdAt": "2026-09-14T18:53:59-05:00",
"publishedAt": "2026-09-14T18:55:56-05:00"
}
}
]
}
}Responses
Status | Meaning |
|---|---|
| The reverb search as JSON. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Response fields
Field | Type | Description |
|---|---|---|
| string | — |
| string | The page we fetched. |
| number | What the call was charged, in credits, fractional (also |
| integer | — |
| object | — |
| string | — |
| integer | — |
| integer or null | — |
| integer or null | Send as |
| string or null | Poshmark only: send as |
| object[] | — |
GET /v1/structured/github/search
GitHub search.
Repositories, users or issues matching a query, with GitHub's own search qualifiers (language:go, stars:>1000, repo:owner/name is:open): stars, forks, topics, followers, comments and more. Up to 100 per page; a search reaches its first 1,000 results.
Cost: charged credits for the data it moves, like every endpoint. Also accepts POST with a JSON body.
Query parameters
Name | Type | Required | Description |
|---|---|---|---|
| string | — | What to search for. |
| string | yes | The search terms and qualifiers, e.g. |
| string | — | Repositories: |
| string | — | With |
| integer | — | Results per page, 1–100. Default |
| integer | — | Page number; |
Request
- cURL
- Python
- Node.js
curl -G "https://api.scrapelyserp.com/v1/structured/github/search" \
-H "Authorization: Bearer $SCRAPELY_API_KEY" \
-d type=repositories \
--data-urlencode "query=web scraper language:go" \
-d sort=starsimport os
import requests
HEADERS = {"Authorization": f"Bearer {os.environ['SCRAPELY_API_KEY']}"}
r = requests.get(
"https://api.scrapelyserp.com/v1/structured/github/search",
headers=HEADERS,
params={
"type": "repositories",
"query": "web scraper language:go",
"sort": "stars",
},
timeout=30,
)
r.raise_for_status()
data = r.json()const res = await fetch(`https://api.scrapelyserp.com/v1/structured/github/search?${new URLSearchParams({
type: 'repositories',
query: 'web scraper language:go',
sort: 'stars',
})}`, {
headers: { Authorization: `Bearer ${process.env.SCRAPELY_API_KEY}` },
signal: AbortSignal.timeout(30_000),
});
if (!res.ok) throw new Error((await res.json()).error.code);
const data = await res.json();Response
{
"endpoint": "github/search",
"url": "https://github.com/search?q=web+scraper+language%3Ago&type=repositories",
"credits": 0.000028,
"duration_ms": 1480,
"data": {
"type": "repositories",
"query": "web scraper language:go",
"sort": "stars",
"order": "desc",
"page": 1,
"total_results": 1377,
"results": [
{
"id": 138322040,
"name": "go-rod/rod",
"owner": "go-rod",
"url": "https://github.com/go-rod/rod",
"description": "A Chrome DevTools Protocol driver for web automation and scraping.",
"homepage": "https://go-rod.github.io",
"language": "Go",
"stars": 7145,
"forks": 400,
"open_issues": 100,
"license": "MIT",
"topics": ["automation", "scraping"],
"archived": false,
"fork": false,
"created_at": "2018-06-22T07:12:51Z",
"updated_at": "2026-11-05T08:00:00Z",
"pushed_at": "2026-11-01T10:00:00Z"
}
],
"next_page": 2
}
}Responses
Status | Meaning |
|---|---|
| The github search as JSON. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Response fields
Field | Type | Description |
|---|---|---|
| string | — |
| string | The page we fetched. |
| number | What the call was charged, in credits, fractional (also |
| integer | — |
| object | — |
| string | One of: |
| string | — |
| string or null | — |
| string or null | — |
| integer | — |
| integer | Everything that matches; only the first 1,000 can be paged to. |
| any[] | — |
| integer or null | The next |
GET /v1/structured/github/lookup
GitHub lookup.
Users and repositories by name, up to 50 per call: a login (torvalds) is a profile, owner/repo (vercel/next.js) is a repository. Followers, stars, forks, license, topics and dates.
Cost: charged credits for the data it moves, like every endpoint. Also accepts POST with a JSON body.
Query parameters
Name | Type | Required | Description |
|---|---|---|---|
| string | yes | Logins and |
Request
- cURL
- Python
- Node.js
curl -G "https://api.scrapelyserp.com/v1/structured/github/lookup" \
-H "Authorization: Bearer $SCRAPELY_API_KEY" \
--data-urlencode "names=torvalds,vercel/next.js"import os
import requests
HEADERS = {"Authorization": f"Bearer {os.environ['SCRAPELY_API_KEY']}"}
r = requests.get(
"https://api.scrapelyserp.com/v1/structured/github/lookup",
headers=HEADERS,
params={"names": "torvalds,vercel/next.js"},
timeout=30,
)
r.raise_for_status()
data = r.json()const res = await fetch(`https://api.scrapelyserp.com/v1/structured/github/lookup?${new URLSearchParams({ names: 'torvalds,vercel/next.js' })}`, {
headers: { Authorization: `Bearer ${process.env.SCRAPELY_API_KEY}` },
signal: AbortSignal.timeout(30_000),
});
if (!res.ok) throw new Error((await res.json()).error.code);
const data = await res.json();Response
{
"endpoint": "github/lookup",
"url": "https://github.com/torvalds",
"credits": 0.000028,
"duration_ms": 1480,
"data": {
"results": [
{
"lookup": "torvalds",
"kind": "user",
"found": true,
"id": 1024025,
"login": "torvalds",
"url": "https://github.com/torvalds",
"type": "User",
"name": "Linus Torvalds",
"company": "Linux Foundation",
"location": "Portland, OR",
"blog": null,
"bio": null,
"followers": 330520,
"following": 0,
"public_repos": 12,
"created_at": "2011-09-03T15:26:22Z"
},
{
"lookup": "nobody-zz/none",
"kind": "repository",
"found": false
}
]
}
}Responses
Status | Meaning |
|---|---|
| The github lookup as JSON. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Response fields
Field | Type | Description |
|---|---|---|
| string | — |
| string | The page we fetched. |
| number | What the call was charged, in credits, fractional (also |
| integer | — |
| object | — |
| object[] | One entry per name, in the order sent. |
GET /v1/structured/stackexchange/search
Stack Exchange search.
Questions on Stack Overflow and other Stack Exchange sites matching a query and/or tags: title, score, answers, views, tags, author and dates. Up to 100 per page; page on with page.
Cost: charged credits for the data it moves, like every endpoint. Also accepts POST with a JSON body.
Query parameters
Name | Type | Required | Description |
|---|---|---|---|
| string | — | Which Stack Exchange site. One of: |
| string | — | The search terms. Send this, |
| string | — | Only questions with all of these tags, up to 5, comma-separated, e.g. |
| string | — | Result order. One of: |
| string | — | The direction. One of: |
| integer | — | Results per page, 1–100. Default |
| integer | — | Page number, 1–25. Default |
Request
- cURL
- Python
- Node.js
curl -G "https://api.scrapelyserp.com/v1/structured/stackexchange/search" \
-H "Authorization: Bearer $SCRAPELY_API_KEY" \
-d site=stackoverflow \
--data-urlencode "query=web scraping" \
-d sort=votesimport os
import requests
HEADERS = {"Authorization": f"Bearer {os.environ['SCRAPELY_API_KEY']}"}
r = requests.get(
"https://api.scrapelyserp.com/v1/structured/stackexchange/search",
headers=HEADERS,
params={"site": "stackoverflow", "query": "web scraping", "sort": "votes"},
timeout=30,
)
r.raise_for_status()
data = r.json()const res = await fetch(`https://api.scrapelyserp.com/v1/structured/stackexchange/search?${new URLSearchParams({ site: 'stackoverflow', query: 'web scraping', sort: 'votes' })}`, {
headers: { Authorization: `Bearer ${process.env.SCRAPELY_API_KEY}` },
signal: AbortSignal.timeout(30_000),
});
if (!res.ok) throw new Error((await res.json()).error.code);
const data = await res.json();Response
{
"endpoint": "stackexchange/search",
"url": "https://stackoverflow.com/search?q=web+scraping",
"credits": 0.000028,
"duration_ms": 1480,
"data": {
"site": "stackoverflow",
"query": "web scraping",
"tagged": [],
"sort": "votes",
"order": "desc",
"page": 1,
"results": [
{
"id": 450835,
"title": "Stopping scripters from slamming your website",
"url": "https://stackoverflow.com/questions/450835/stopping-scripters-from-slamming-your-website",
"score": 506,
"answers": 129,
"views": 93898,
"is_answered": true,
"accepted_answer_id": 450905,
"tags": ["security", "scripting"],
"author": "Zack Peterson",
"author_id": 1,
"author_reputation": 4059,
"created_at": "2009-01-16T14:34:00.000Z",
"last_activity_at": "2026-10-01T09:00:00.000Z"
}
],
"next_page": 2
}
}Responses
Status | Meaning |
|---|---|
| The stack exchange search as JSON. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Response fields
Field | Type | Description |
|---|---|---|
| string | — |
| string | The page we fetched. |
| number | What the call was charged, in credits, fractional (also |
| integer | — |
| object | — |
| string | — |
| string or null | — |
| string[] | — |
| string | — |
| string | — |
| integer | — |
| object[] | — |
| integer or null | The next |
GET /v1/structured/stackexchange/questions
Stack Exchange questions.
Questions by id, up to 100 per call, each with its body as HTML: title, score, answers, views, tags, author and dates. Ids that do not exist come back in missing.
Cost: charged credits for the data it moves, like every endpoint. Also accepts POST with a JSON body.
Query parameters
Name | Type | Required | Description |
|---|---|---|---|
| string | — | Which Stack Exchange site. One of: |
| string | yes | Question ids, comma-separated, up to 100. A question link is accepted. |
Request
- cURL
- Python
- Node.js
curl -G "https://api.scrapelyserp.com/v1/structured/stackexchange/questions" \
-H "Authorization: Bearer $SCRAPELY_API_KEY" \
-d site=stackoverflow \
--data-urlencode "ids=450835,16995184"import os
import requests
HEADERS = {"Authorization": f"Bearer {os.environ['SCRAPELY_API_KEY']}"}
r = requests.get(
"https://api.scrapelyserp.com/v1/structured/stackexchange/questions",
headers=HEADERS,
params={"site": "stackoverflow", "ids": "450835,16995184"},
timeout=30,
)
r.raise_for_status()
data = r.json()const res = await fetch(`https://api.scrapelyserp.com/v1/structured/stackexchange/questions?${new URLSearchParams({ site: 'stackoverflow', ids: '450835,16995184' })}`, {
headers: { Authorization: `Bearer ${process.env.SCRAPELY_API_KEY}` },
signal: AbortSignal.timeout(30_000),
});
if (!res.ok) throw new Error((await res.json()).error.code);
const data = await res.json();Response
{
"endpoint": "stackexchange/questions",
"url": "https://stackoverflow.com/questions/450835",
"credits": 0.000028,
"duration_ms": 1480,
"data": {
"site": "stackoverflow",
"results": [
{
"id": 450835,
"title": "Stopping scripters from slamming your website",
"url": "https://stackoverflow.com/questions/450835/stopping-scripters-from-slamming-your-website",
"score": 506,
"answers": 129,
"views": 93898,
"is_answered": true,
"accepted_answer_id": 450905,
"tags": ["security", "scripting"],
"author": "Zack Peterson",
"author_id": 1,
"author_reputation": 4059,
"created_at": "2009-01-16T14:34:00.000Z",
"last_activity_at": "2026-10-01T09:00:00.000Z",
"body_html": "<p>I have a website that scripters are slamming…</p>"
}
],
"missing": []
}
}Responses
Status | Meaning |
|---|---|
| The stack exchange questions as JSON. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Response fields
Field | Type | Description |
|---|---|---|
| string | — |
| string | The page we fetched. |
| number | What the call was charged, in credits, fractional (also |
| integer | — |
| object | — |
| string | — |
| object[] | In the order the ids were sent. |
| integer[] | Ids that do not exist (or were deleted). |