zytedata/claude-skills · Archived

scrape-scrapy-cloud

General-purpose Scrapy Cloud skill — deploy projects, schedule spiders, list/stop jobs, and view items or logs.

First seen Jun 15, 2026

Installation

$ npx skills add zytedata/claude-skills --skill scrape-scrapy-cloud

Summary

  • General-purpose Scrapy Cloud skill — deploy projects, schedule spiders, list/stop jobs, and view items or logs.
  • Use when asked to deploy a project or spider to Scrapy Cloud / Zyte Cloud / Scrapinghub, schedule or run a spider remotely, manage jobs, or inspect scraped items and logs.

Stronger alternatives

This repository is archived — consider an actively maintained alternative.

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from zytedata/claude-skills · top by installs.

npx skills add zytedata/claude-skills

Browse all from zytedata/claude-skills

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Repository health

Stars 25
License LICENSE.md
Default branch main
Open issues 0
Status Archived

Skill metadata

Parsed from SKILL.md frontmatter.

Allowed toolsBash, Read, Write, WebFetch

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 22,957 B
  • docs SUMMARY.md 312 B

History

  1. First seen on skills.sh
  2. First recorded snapshot · 4 installs

SKILL.md

You are a general-purpose Scrapy Cloud assistant. You can deploy projects, schedule spiders, manage jobs (list, stop), and direct users to the web UI to inspect items and logs — all using shub and the Scrapy Cloud HTTP API.

Read python-environments.md and docs-access.md from ${CLAUDESKILLDIR}/../scrape/references.

Rules

  • DO NOT prematurely ask the user for project ID, endpoint, credentials, or deployment

target. All of these are discovered by following the Process steps below (checking scrapinghub.yml, ~/.scrapinghub.yml, and environment variables).

  • Execute the Process steps sequentially — only ask the user when a step

explicitly says to.

  • If the user's open file or current directory contains a scrapinghub.yml,

use it to determine which project to deploy.

Input

The raw argument string is $ARGUMENTS — use it as-is, treat empty as "no argument given":

  1. project_dir: path to the Scrapy project directory (defaults to current directory if the argument string is empty)

Process

IMPORTANT: It's critical that API keys are not displayed or exposed to the agent or user during a session. Do not use any tool that might print the key or its value (e.g. cat ~/.scrapinghub.yml) as an auth probe.

1. Locate the project root

Navigate to project_dir (or the current directory if none was given). Confirm scrapy.cfg exists:

ls scrapy.cfg

If scrapy.cfg is not in the current directory, walk up the directory tree to find it. Once found, cd into that directory — all subsequent commands must run from there.

2. Check for existing scrapinghub.yml

ls scrapinghub.yml 2>/dev/null || echo "(not found)"

If the file exists skip step 3 and proceed directly to step 4.

3. Create scrapinghub.yml

Get the project ID

Check for a saved project ID from a previous scrape-zyte-login:

cat .scrape/.zyte/project-id 2>/dev/null || echo "(not set)"

If present, use it and skip asking.

Otherwise, open the Zyte projects page:

xdg-open "https://app.zyte.com/o/projects/" 2>/dev/null || open "https://app.zyte.com/o/projects/"

Ask the user:

Please open https://app.zyte.com/o/projects/ and provide the numeric project ID
for this spider (e.g. 12345). If you don't have a project yet, create one first.

Wait for the user to supply a numeric project ID.

Prepare requirements

If requirements.txt already exists, use it as-is.

Otherwise, look for a dependency specification (any standard file declaring Python dependencies). If one exists, generate a freeze requirements.txt (all packages pinned with ==) from it by automatic means. If none exists, identify the third-party packages from the project source, write them to a non-freeze dependency file, then generate the freeze requirements.txt from that file by automatic means.

In all cases, scrapinghub.yml points at requirements.txt.

If requirements.txt did not exist before, tell the user the exact command used to generate it, and what to run when dependencies change or they wish to upgrade them.

Specify a Scrapy stack

Use the Docker Hub API or scrape the tags page to find the most recent stack tag for the scrapinghub/scrapinghub-stack-scrapy repository that matches the Scrapy version in the requirements file:

  • API endpoint: https://hub.docker.com/v2/repositories/scrapinghub/scrapinghub-stack-scrapy/tags
  • Web page: https://hub.docker.com/r/scrapinghub/scrapinghub-stack-scrapy/tags

Tags follow the format {VERSION}-{YYYYMMDD} (e.g. 2.14-20260326). Select the tag with the highest version number and, among equal versions, the latest frozen date. Use that tag as the stack value in scrapinghub.yml, prefixed with scrapy: (e.g. scrapy:2.14-20260326). If the requirements file doesn't specify a Scrapy version, or no stack tag matches the version it specifies, use the latest tag overall.

Write scrapinghub.yml

Create the file. If SCRAPYCLOUDENDPOINT is set, add an endpoint: key.

project: 12345

stack: scrapy:2.14-20260326  # replace with the latest tag fetched above

requirements:
  file: requirements.txt

endpoint: https://app-staging.zyte.com/api/  # include when SCRAPY_CLOUD_ENDPOINT is set

4. Deploy

Run the deploy from the project root with uvx, which fetches shub on demand — no separate install step, and it works whether or not shub is already on PATH:

uvx shub deploy

Stream and display the full output. A successful deploy looks like:

Packing version 3af023e-master
Deploying to Scrapy Cloud project "12345"
{"status": "ok", "project": 12345, "version": "3af023e-master", "spiders": 2}
Run your spiders at: https://app.zyte.com/p/12345/

On failure, diagnose the error and fix before retrying:

  • Error: Not logged in. Please run 'shub login' first. → not authenticated; invoke /scrape-zyte-login.
  • Error: Invalid value for target: Please specify target or configure a default target in scrapinghub.yml. → no project ID configured; ask the user for a project ID, update scrapinghub.yml and retry.
  • Authentication error / 403 → API key is wrong or missing; invoke /scrape-zyte-login.
  • Project N does not exist → wrong project ID; correct scrapinghub.yml and retry.
  • Could not find requirements file → the requirements.file path in scrapinghub.yml is wrong; fix the path and retry.
  • No module named scrapy or build errors → a dependency is missing or incompatible with the selected stack; update the requirements file and retry.
  • Any other error → show the full error message to the user and ask how to proceed.

5. Report success

Tell the user the deploy succeeded, and include the version, spider count, and project link (https://app.zyte.com/p/<project>/).

If the stack's Scrapy version is older than the version in requirements.txt, strongly recommend running a test job, and note that if issues arise, stack incompatibility is a possible — not assumed — root cause.


Scheduling a job

Use uvx shub schedule to start a job that runs a spider on Scrapy Cloud without redeploying.

Tag jobs with skill.

Basic schedule

uvx shub schedule SPIDER --tag skill

On success, shub prints the job ID and convenience links:

Spider myspider scheduled, job ID: 12345/2/15
Watch the log on the command line:
    shub log -f 2/15
or watch it running in Zyte's web interface:
    https://app.zyte.com/p/12345/job/2/15

With spider arguments (-a)

Repeat -a KEY=VALUE for each argument:

uvx shub schedule myspider -a ARG1=VALUE1 -a ARG2=VALUE2 --tag skill

With job-specific Scrapy settings (-s)

uvx shub schedule myspider -s LOG_LEVEL=DEBUG -s CLOSESPIDER_PAGECOUNT=10 --tag skill

With a specific project

uvx shub schedule 33333/myspider --tag skill

With units and priority

Flag Description
-u Number of Scrapy Cloud units to use (1–6)
-p Job priority: 0 (lowest) to 4 (highest)
uvx shub schedule myspider -p 3 -u 3 --tag skill

Combined example

uvx shub schedule myspider \
  -a start_url=https://example.com \
  -s LOG_LEVEL=DEBUG \
  -p 2 \
  -u 1 \
  --tag skill

Waiting for a job to finish

After scheduling a job, wait for it to reach a terminal state so you can validate its results (see the next section). Use the bundled helper — it polls the Jobs API with backoff so you don't have to poll by hand:

uv run ${CLAUDE_SKILL_DIR}/scripts/wait_for_job.py PROJECT/SPIDER/JOB

Optional flags: --poll-interval SECONDS (initial gap, default 10, backs off to 60) and --max-wait SECONDS (give up after this long, default 1200).

It blocks until the job reaches a terminal state (or --max-wait is hit), then prints exactly one machine-readable line to stdout:

  • JOB_FINISHED {...job json...} — the job reached a terminal state (exit 0).

Parse the JSON for state, closereason, errorscount, items_scraped, etc., then proceed to validation.

  • JOB_TIMEOUT {...last known job json...} — --max-wait elapsed while the

job was still running (exit 1). Tell the user the job is still running and offer to keep waiting (re-run with a larger --max-wait) or check back later.

Progress lines go to stderr and can be ignored.

Validating job results

Do this by default after running a job — deploy/schedule is not "done" until the results look right. Skip it only if the user opted out (phrases like "just schedule it", "don't wait for it", "no need to check the results"): in that case, report the job link and stop.

Work through these checks against the finished job:

1. Errors and close reason

From the JOBFINISHED summary, closereason should be "finished". Anything else — failed, cancelled, or an unexpected closespider* reason — is a flag worth investigating. Treat errorscount as a signal, not proof: a 0 count does not guarantee a clean run if the log level suppressed errors (see step 3).

2. Inspect logs locally

Download the full log once and grep/jq it locally — far faster than paginating the HTTP API line by line. Logs come back as JSON Lines by default:

uv run ${CLAUDE_SKILL_DIR}/scripts/scrapy_cloud_api.py GET \
  "${SCRAPY_CLOUD_STORAGE_ENDPOINT:-https://storage.zyte.com/}logs/PROJECT/SPIDER/JOB" > job.log.jl
# ERROR (40) and CRITICAL (50) entries:
jq -c 'select(.level >= 40)' job.log.jl

Each entry is {"time": <unix-ms>, "level": <int>, "message": <str>}; see the log-level table under "Viewing items and logs". For pagination/format options, see the logs API docs linked there.

If the project uses Zyte API (via scrapy-zyte-api), the Scrapy stats dumped at close include scrapy-zyte-api/ counters — success/error ratios, 429s, error_types/, bans. These often explain a bad close reason or low coverage, and are easy to misread. For what each counter means, see https://scrapy-zyte-api.readthedocs.io/en/latest/reference/stats.md

3. Log level

No DEBUG messages in the logs means LOGLEVEL is set above DEBUG. When a job has issues you can't diagnose from the available logs, rerun it with LOGLEVEL=DEBUG.

LOG_LEVEL can be set in code or in Scrapy Cloud's project/spider settings. If setting it in code has no effect, cloud settings are overriding it — there's no public API to read or change them, so ask the user to fix it manually.

4. Field coverage

Use the item stats endpoint (see "Item stats" below) to get per-field population counts without downloading every item. Compare counts[field] against totals.input_values (total item count). Determine which fields were expected:

  • If this job is part of the /scrape workflow, the expected fields are in

.scrape/{site}/{data-type}/spec.json (see ${CLAUDESKILLDIR}/../scrape/references/extraction-spec.md).

  • Otherwise, read the item / page-object class in the deployed project.

Flag any expected field at 0% coverage.

For fields between 0% and 100% coverage, download a few items where the field is missing (see "Downloading items" below) and judge whether it's legitimately absent or an extraction bug.

5. Spot-check that items match the request

Download a small sample and read it (see "Downloading items" below):

uv run ${CLAUDE_SKILL_DIR}/scripts/scrapy_cloud_api.py GET \
  "${SCRAPY_CLOUD_STORAGE_ENDPOINT:-https://storage.zyte.com/}items/PROJECT/SPIDER/JOB" -q count=5

Judge whether the sampled items match what the user actually asked for — the right categories, filters, or product types (from the spec's schema/examples and start URLs, or from the conversation). This is a judgment call, not a scripted assertion.

6. Report

Summarize: job link, close_reason, error count, a short field-coverage table, and the sample check verdict.

On a finding

If any check fails (errors, low coverage, wrong-category items), diagnose the likely cause, fix the project code, redeploy (uvx shub deploy), reschedule, and re-validate. Cap this at 3 total attempts — matching the local validation loop in scrape-create-spider. If it still looks wrong after 3, stop and report what remains broken rather than looping further.


Managing jobs

Use the Scrapy Cloud Jobs HTTP API for listing and stopping jobs.

Base URL: ${SCRAPYCLOUDENDPOINT:-https://app.zyte.com/api/}

Wrapper script

IMPORTANT It is critical that API requests are made with the wrapper script at scripts/scrapycloudapi.py which handles authentication without leaking credentials to the agent. Do not make API requests with curl or other tools that might expose credentials.

uv run ${CLAUDE_SKILL_DIR}/scripts/scrapy_cloud_api.py HTTP_METHOD API_URL [-q QUERY_ARG=VALUE]... [-b BODY_ARG=VALUE]...

With arbitrary query parameters (-q) and body parameters (-b) as needed per endpoint. See the script's help message for details.

Output format: the script prints the HTTP status code on the first line, then the response body on the remaining lines. Use the status line to detect errors (e.g. 401/403 auth failures); when you only need the body, ignore the first line. The response bodies shown below omit the leading status line.

List jobs

# All running jobs
uv run ${CLAUDE_SKILL_DIR}/scripts/scrapy_cloud_api.py GET "${SCRAPY_CLOUD_ENDPOINT:-https://app.zyte.com/api/}jobs/list.json" -q project=PROJECT_ID -q state=running

# Latest 3 finished jobs for a specific spider
uv run ${CLAUDE_SKILL_DIR}/scripts/scrapy_cloud_api.py GET "${SCRAPY_CLOUD_ENDPOINT:-https://app.zyte.com/api/}jobs/list.json" -q project=PROJECT_ID -q spider=SPIDER_NAME -q state=finished -q count=3

# Jobs that lack the "consumed" tag
uv run ${CLAUDE_SKILL_DIR}/scripts/scrapy_cloud_api.py GET "${SCRAPY_CLOUD_ENDPOINT:-https://app.zyte.com/api/}jobs/list.json" -q project=PROJECT_ID -q lacks_tag=consumed

Available state values: pending, running, finished, deleted. Available filter parameters: job, spider, state, hastag, lackstag, count.

Stop a job

uv run ${CLAUDE_SKILL_DIR}/scripts/scrapy_cloud_api.py POST "${SCRAPY_CLOUD_ENDPOINT:-https://app.zyte.com/api/}jobs/stop.json" -b project=PROJECT_ID -b job=PROJECT_ID/SPIDER_ID/JOB_ID

Viewing items and logs

Direct the user to the Scrapy Cloud web UI to inspect items and logs.

Derive the web UI base URL from SCRAPYCLOUDENDPOINT by stripping the trailing /api/ path (default: https://app.zyte.com).

Items page

${SCRAPY_CLOUD_ENDPOINT%api/}p/PROJECT_ID/SPIDER_ID/JOB_ID/items

Open with:

BASE_UI="${SCRAPY_CLOUD_ENDPOINT%api/}"
xdg-open "${BASE_UI:-https://app.zyte.com/}p/PROJECT_ID/SPIDER_ID/JOB_ID/items" 2>/dev/null \
  || open "${BASE_UI:-https://app.zyte.com/}p/PROJECT_ID/SPIDER_ID/JOB_ID/items"

Item stats (HTTP API)

For a quick summary of item counts and field coverage without downloading all items, use the stats endpoint directly:

uv run ${CLAUDE_SKILL_DIR}/scripts/scrapy_cloud_api.py GET "${SCRAPY_CLOUD_ENDPOINT:-https://app.zyte.com/api/}items/PROJECT_ID/SPIDER_ID/JOB_ID/stats"
# Response: {"counts":{"field1":9350,"field2":514},"totals":{"input_bytes":14390294,"input_values":10000}}

Response fields:

Field Description
counts[field] Number of times each field was populated.
totals.input_bytes Total size of all items in bytes.
totals.input_values Total number of items.

Downloading items (HTTP API)

To read the items themselves, fetch them from the storage API through the wrapper script (never curl — the wrapper keeps credentials out of the trajectory). The default response format is JSON Lines, one item per line:

# All items
uv run ${CLAUDE_SKILL_DIR}/scripts/scrapy_cloud_api.py GET "${SCRAPY_CLOUD_STORAGE_ENDPOINT:-https://storage.zyte.com/}items/PROJECT_ID/SPIDER_ID/JOB_ID"

# A bounded sample (recommended for spot-checks)
uv run ${CLAUDE_SKILL_DIR}/scripts/scrapy_cloud_api.py GET "${SCRAPY_CLOUD_STORAGE_ENDPOINT:-https://storage.zyte.com/}items/PROJECT_ID/SPIDER_ID/JOB_ID" -q count=5

# Specific items by index (repeat -q index=N for each)
uv run ${CLAUDE_SKILL_DIR}/scripts/scrapy_cloud_api.py GET "${SCRAPY_CLOUD_STORAGE_ENDPOINT:-https://storage.zyte.com/}items/PROJECT_ID/SPIDER_ID/JOB_ID" -q index=0 -q index=1

Redirect to a file and inspect locally with jq/grep for anything beyond a quick look. For pagination (start, startafter), field filtering, gzip, and other formats (-q format=json), see the items API docs linked below.

Log page

${SCRAPY_CLOUD_ENDPOINT%api/}p/PROJECT_ID/SPIDER_ID/JOB_ID/log

Open with:

BASE_UI="${SCRAPY_CLOUD_ENDPOINT%api/}"
xdg-open "${BASE_UI:-https://app.zyte.com/}p/PROJECT_ID/SPIDER_ID/JOB_ID/log" 2>/dev/null \
  || open "${BASE_UI:-https://app.zyte.com/}p/PROJECT_ID/SPIDER_ID/JOB_ID/log"

Log levels

Logs returned by the HTTP API include a numeric level field:

Value Level
10 DEBUG
20 INFO
30 WARNING
40 ERROR
50 CRITICAL

Each log entry is a JSON object with fields: time (Unix ms), level, and message.

Reference docs

The Scrapy Cloud docs are available as Markdown (swap .html → .md). Prefer fetching these over guessing at API mechanics — they cover the exhaustive set of parameters, formats, and fields:


When to Use

Invoke this skill when the user asks to:

  • Deploy a Scrapy project or spider to the cloud
  • Run a spider on Scrapy Cloud / Zyte Cloud / Scrapinghub Cloud
  • Upload a project to Zyte / Scrapinghub
  • Schedule a spider remotely (with arguments, settings, priority, or units)
  • Push code to Scrapy Cloud
  • List, filter, or inspect jobs
  • Stop a running job
  • View scraped items or logs for a job
  • Wait for a running job to finish
  • Validate or check the results of a job (errors, field coverage, item quality)

Example phrases that should trigger this skill:

  • "Deploy my project"
  • "Deploy my spider to Scrapy Cloud"
  • "I want to run my spider in the cloud"
  • "How do I get my spider running on Scrapy Cloud?"
  • "Upload my Scrapy project to Zyte"
  • "Push my project to Zyte"
  • "I want to schedule my spider remotely"
  • "Schedule myspider on Scrapy Cloud"
  • "List all running jobs"
  • "Stop job 123/1/5"
  • "Show items from job 123/1/5"
  • "Show logs for job 123/1/5"
  • "Run my spider on Scrapy Cloud and check the results"
  • "Wait for job 123/1/5 and validate the output"

Environment Variables

Variable Default Description
SHUB_APIKEY (none) Scrapy Cloud API key; falls back to ~/.scrapinghub.yml
SCRAPYCLOUDENDPOINT https://app.zyte.com/api/ Jobs API base URL (override for staging)
SCRAPYCLOUDSTORAGE_ENDPOINT https://storage.zyte.com/ Items/logs storage base URL (override for staging)

The web UI base URL is derived from SCRAPYCLOUDENDPOINT by stripping the trailing /api/ path. For example:

  • https://app.zyte.com/api/ → https://app.zyte.com
  • https://app-staging.zyte.com/api/ → https://app-staging.zyte.com

Common issues

Symptom Cause Fix
Error: No such command / shub not found shub invoked directly but not installed Invoke it as uvx shub … — fetched on demand, no install
Authentication error / 401 API key missing or invalid Run shub login or set $SHUB_APIKEY
403 API key lacks access to this project Verify the project ID and key permissions
Spider not found Spider name is wrong or project not deployed Verify the spider name; deploy with uvx shub deploy first
Project N does not exist Wrong project ID or alias Check scrapinghub.yml or specify the correct ID
Could not find requirements file Wrong path in scrapinghub.yml Fix the requirements.file path and redeploy
No module named scrapy / build errors Dependency missing or wrong stack Update requirements file and redeploy
sh_scrapy errors in job logs Stack's scrapinghub-entrypoint-scrapy may not support the Scrapy version in requirements.txt If a newer version of scrapinghub-entrypoint-scrapy exists on PyPI than the one bundled in the stack, add it to the dependency specification, regenerate requirements.txt, and redeploy
close_reason is not finished Job failed, was cancelled, or hit a closespider_* limit Read the logs (filter level >= 40); fix the root cause and reschedule. See "Validating job results"
A field has 0% or unexpectedly low coverage Broken/incorrect selector in the page object Inspect the item stats and a sample of items, fix the page object, redeploy, and re-run. See "Validating job results"