S3 export
Write every watchlist event to your own S3 bucket (or any S3-compatible store: Cloudflare R2, Wasabi, MinIO, Backblaze B2). One destination per account. The secret access key is write-only: it is stored encrypted and never returned.
Write every watchlist event to your own S3 bucket (or any S3-compatible store: Cloudflare R2, Wasabi, MinIO, Backblaze B2). One destination per account. The secret access key is write-only: it is stored encrypted and never returned.
Endpoints
Method | Path | Does |
|---|---|---|
|
| |
|
| |
|
| |
|
|
How it works, when to use what, and what it costs: S3 export. Base URL, authentication and errors: Overview.
GET /v1/exports/s3
Get the S3 export destination.
The destination with its access key id masked (the secret is never returned), the last connection test, and the 20 most recent uploads. 404 when the account has none.
Request
- cURL
- Python
- Node.js
curl "https://api.scrapelyserp.com/v1/exports/s3" \
-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/exports/s3",
headers=HEADERS,
timeout=30,
)
r.raise_for_status()
data = r.json()const res = await fetch('https://api.scrapelyserp.com/v1/exports/s3', {
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
- 200 OK
- 404 Not Found
{
"object": "s3_destination",
"bucket": "my-scrapes",
"region": "us-east-1",
"endpoint": null,
"path_style": false,
"prefix": "scrapely/",
"enabled": true,
"access_key_id": "••••EYID",
"last_test": {
"at": "2026-10-16T08:59:58.000Z",
"ok": true,
"error": null
},
"created_at": "2026-10-06T08:00:00.000Z",
"updated_at": "2026-10-16T09:00:00.000Z",
"recent_exports": [
{
"id": "s3x_8f7e6d5c4b3a2910",
"kind": "watchlist",
"key": "scrapely/watchlists/wl_3f1c2a9e8d4b4c61/wlrun_5f0c8a1b2d3e4f56/watchlist.product.changed-1.json",
"status": "ok",
"status_code": 200,
"error": null,
"bytes": 2381,
"attempt": 1,
"at": "2026-10-16T09:15:03.002Z"
}
]
}{
"error": {
"code": "NOT_FOUND",
"message": "This account has no S3 export destination. Create one with PUT /v1/exports/s3.",
"docs": "/docs/errors#not_found"
}
}Responses
Status | Meaning |
|---|---|
| The destination (keys masked) and its 20 most recent uploads. |
|
|
|
|
|
|
|
|
Response fields
Field | Type | Description |
|---|---|---|
| "s3_destination" | — |
| string | — |
| string | — |
| string or null | null: AWS ( |
| boolean |
|
| string | — |
| boolean | On: every watchlist event is written to |
| string | Masked: the last four characters only. The secret access key is never returned. |
| object or null | — |
| string (date-time) | — |
| boolean | — |
| string or null | — |
| string (date-time) | — |
| string (date-time) | — |
| object[] | — |
| string | — |
| string | One of: |
| string | — |
| string | One of: |
| integer or null | — |
| string or null | — |
| integer | — |
| integer | — |
| string (date-time) | — |
PUT /v1/exports/s3
Create or update the S3 export destination.
Fields left out keep their current value. Runs a connection test first (unless test: false); enabled: true is refused with a 400 when the test fails, and nothing is saved.
Request body
Name | Type | Required | Description |
|---|---|---|---|
| string | — | S3 bucket naming rules: 3–63 lowercase letters, numbers, dots and hyphens. |
| string | — | An AWS region (us-east-1, us-east-2, us-west-1, us-west-2, …). With |
| string | — | Send with |
| string | — | Write-only. Stored encrypted (AES-256-GCM) and never returned. Write-only: never returned. |
| string | — | Key prefix for everything written. |
| string or null | — | An S3-compatible service: https only, a public host, port 443 or 1024–65535, no path. null for AWS. |
| boolean | — | Default: true with |
| boolean | — |
|
| boolean | — | Write |
Request
- cURL
- Python
- Node.js
curl -X PUT "https://api.scrapelyserp.com/v1/exports/s3" \
-H "Authorization: Bearer $SCRAPELY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"bucket": "my-scrapes",
"region": "us-east-1",
"access_key_id": "EXAMPLEKEYID",
"secret_access_key": "example-secret-access-key",
"prefix": "scrapely/"
}'import os
import requests
HEADERS = {"Authorization": f"Bearer {os.environ['SCRAPELY_API_KEY']}"}
r = requests.put(
"https://api.scrapelyserp.com/v1/exports/s3",
headers=HEADERS,
json={
"bucket": "my-scrapes",
"region": "us-east-1",
"access_key_id": "EXAMPLEKEYID",
"secret_access_key": "example-secret-access-key",
"prefix": "scrapely/",
},
timeout=30,
)
r.raise_for_status()
data = r.json()const res = await fetch('https://api.scrapelyserp.com/v1/exports/s3', {
method: 'PUT',
headers: {
Authorization: `Bearer ${process.env.SCRAPELY_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
bucket: 'my-scrapes',
region: 'us-east-1',
access_key_id: 'EXAMPLEKEYID',
secret_access_key: 'example-secret-access-key',
prefix: 'scrapely/',
}),
signal: AbortSignal.timeout(30_000),
});
if (!res.ok) throw new Error((await res.json()).error.code);
const data = await res.json();Response
- 200 OK
- 201 Created
- 400 Bad Request
{
"object": "s3_destination",
"bucket": "my-scrapes",
"region": "us-east-1",
"endpoint": null,
"path_style": false,
"prefix": "scrapely/",
"enabled": true,
"access_key_id": "••••EYID",
"last_test": {
"at": "2026-10-16T09:15:04.120Z",
"ok": true,
"error": null
},
"created_at": "2026-10-06T08:00:00.000Z",
"updated_at": "2026-10-16T09:15:04.120Z",
"test": {
"ok": true,
"key": "scrapely/scrapely-connection-test.txt",
"status_code": 200,
"code": null,
"error": null
}
}{
"object": "s3_destination",
"bucket": "my-scrapes",
"region": "us-east-1",
"endpoint": null,
"path_style": false,
"prefix": "scrapely/",
"enabled": true,
"access_key_id": "••••EYID",
"last_test": {
"at": "2026-10-16T09:15:04.120Z",
"ok": true,
"error": null
},
"created_at": "2026-10-16T09:15:04.120Z",
"updated_at": "2026-10-16T09:15:04.120Z",
"test": {
"ok": true,
"key": "scrapely/scrapely-connection-test.txt",
"status_code": 200,
"code": null,
"error": null
}
}{
"error": {
"code": "MISSING_PARAM",
"message": "Missing required parameters `access_key_id` and `secret_access_key`.",
"param": "access_key_id",
"docs": "/docs/errors#missing_param"
}
}Responses
Status | Meaning |
|---|---|
| Updated. |
| Created. |
|
|
|
|
|
|
|
|
Response fields
Field | Type | Description |
|---|---|---|
| "s3_destination" | — |
| string | — |
| string | — |
| string or null | null: AWS ( |
| boolean |
|
| string | — |
| boolean | On: every watchlist event is written to |
| string | Masked: the last four characters only. The secret access key is never returned. |
| object or null | — |
| string (date-time) | — |
| boolean | — |
| string or null | — |
| string (date-time) | — |
| string (date-time) | — |
| object or null | — |
| boolean | — |
| string | — |
| integer or null | — |
| string or null | The storage service's error code: |
| string or null | What went wrong, in plain words. |
DELETE /v1/exports/s3
Remove the S3 export destination.
Deletes the destination and its stored keys. Nothing more is written to the bucket; objects already there stay.
Request
- cURL
- Python
- Node.js
curl -X DELETE "https://api.scrapelyserp.com/v1/exports/s3" \
-H "Authorization: Bearer $SCRAPELY_API_KEY"import os
import requests
HEADERS = {"Authorization": f"Bearer {os.environ['SCRAPELY_API_KEY']}"}
r = requests.delete(
"https://api.scrapelyserp.com/v1/exports/s3",
headers=HEADERS,
timeout=30,
)
r.raise_for_status()
data = r.json()const res = await fetch('https://api.scrapelyserp.com/v1/exports/s3', {
method: 'DELETE',
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
- 200 OK
- 404 Not Found
{
"object": "s3_destination",
"deleted": true
}{
"error": {
"code": "NOT_FOUND",
"message": "This account has no S3 export destination. Create one with PUT /v1/exports/s3.",
"docs": "/docs/errors#not_found"
}
}Responses
Status | Meaning |
|---|---|
| Removed. |
|
|
|
|
|
|
|
|
Response fields
Field | Type | Description |
|---|---|---|
| "s3_destination" | — |
| true | — |
POST /v1/exports/s3/test
Test the S3 connection.
Writes <prefix>scrapely-connection-test.txt now. With no body, tests the saved destination; with destination fields (as PUT), tests those merged over it without saving.
Request body (optional)
Name | Type | Required | Description |
|---|---|---|---|
| string | — | S3 bucket naming rules: 3–63 lowercase letters, numbers, dots and hyphens. |
| string | — | An AWS region (us-east-1, us-east-2, us-west-1, us-west-2, …). With |
| string | — | Send with |
| string | — | Write-only. Stored encrypted (AES-256-GCM) and never returned. Write-only: never returned. |
| string | — | Key prefix for everything written. |
| string or null | — | An S3-compatible service: https only, a public host, port 443 or 1024–65535, no path. null for AWS. |
| boolean | — | Default: true with |
| boolean | — |
|
| boolean | — | Write |
Request
- cURL
- Python
- Node.js
curl -X POST "https://api.scrapelyserp.com/v1/exports/s3/test" \
-H "Authorization: Bearer $SCRAPELY_API_KEY"import os
import requests
HEADERS = {"Authorization": f"Bearer {os.environ['SCRAPELY_API_KEY']}"}
r = requests.post(
"https://api.scrapelyserp.com/v1/exports/s3/test",
headers=HEADERS,
timeout=30,
)
r.raise_for_status()
data = r.json()const res = await fetch('https://api.scrapelyserp.com/v1/exports/s3/test', {
method: 'POST',
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
- 200 OK
- 404 Not Found
{
"object": "s3_test",
"ok": true,
"key": "scrapely/scrapely-connection-test.txt",
"status_code": 200,
"code": null,
"error": null,
"duration_ms": 412
}{
"error": {
"code": "NOT_FOUND",
"message": "This account has no S3 export destination. Create one with PUT /v1/exports/s3.",
"docs": "/docs/errors#not_found"
}
}Responses
Status | Meaning |
|---|---|
| What the storage service answered. |
|
|
|
|
|
|
|
|
|
|
Response fields
Field | Type | Description |
|---|---|---|
| "s3_test" | — |
| integer | — |
| boolean | — |
| string | — |
| integer or null | — |
| string or null | The storage service's error code: |
| string or null | What went wrong, in plain words. |