@vibelyster/depop-cli
v0.2.0
Published
CLI for Depop's internal API — list, create, edit, and manage product listings
Readme
@vibelyster/depop-cli
Proof-of-concept CLI for Depop's internal API. Uses impit to mimic Chrome TLS fingerprints and bypass Cloudflare bot detection.
Reverse-engineered from browser DevTools network traffic and verified against live API (2026-03-27). See marketplace API research for full technical details.
Quick Start
# No install needed — run directly
node tools/depop/cli.js --help
# Or link globally
cd tools/depop && npm link
depop --helpAuthentication
Only the bearer token is required. User ID is auto-resolved from the API.
# Option 1: interactive login (saves to ~/.vibelyster/depop.json)
depop login
# Option 2: env var
export DEPOP_ACCESS_TOKEN="your-access-token"
# Option 3: per-command flag
depop auth --access-token "..."To get your access token:
- Log into depop.com in your browser
- Open DevTools → Application → Cookies → depop.com
- Copy the
access_tokencookie value
Note: Depop uses Cloudflare TLS fingerprinting (JA3/JA4) to block non-browser clients. This CLI uses
impit(Rust-based Chrome TLS mimic) to bypass this — no real browser required.
Commands
depop login # Save your access token
depop auth # Check login status
depop logout # Remove saved credentials
depop listings # List your products
depop listing <slug> # Get product details
depop addresses # List shipping addresses
depop upload <image-path> # Upload a square image → {id, url}
depop create <json-file> # Create a draft listing
depop drafts # List draft listings
depop draft-update <id> <json-file> # Update a draft
depop draft-delete <id> # Delete a draft
depop edit <product-id> <json-file> # Edit a live product in-place
depop delete <product-id> # Delete a live productListing Lifecycle
┌──────────────────────────────────────────────────────────────────┐
│ │
│ 1. Upload images depop upload ./photo.jpg │
│ └─ Two-step: POST JSON → presigned S3 URL → PUT image │
│ └─ Returns {id, url} │
│ └─ Images MUST be square (Depop rejects non-square) │
│ │
│ 2. Create draft depop create draft.json │
│ └─ POST /api/v2/drafts/ (direct POST to products fails) │
│ └─ Returns {id} (UUID) │
│ │
│ 3. Manage drafts depop drafts / draft-update / draft-del │
│ └─ GET/PUT /api/v2/drafts/ DELETE /api/v1/drafts/ │
│ │
│ 4. Publish Open draft in browser and click Post │
│ └─ depop.com/sellinghub/drafts/edit/<draft-id>/ │
│ └─ No API-only publish endpoint found yet │
│ │
│ 5. Edit live listing depop edit <id> updates.json │
│ └─ PUT /api/v2/products/<id>/ │
│ │
│ 6. Delete listing depop delete <id> │
│ └─ DELETE /api/v1/products/<id>/ (v1, not v2) │
│ │
└──────────────────────────────────────────────────────────────────┘Cloudflare TLS Bypass
Depop's API (webapi.depop.com) is behind Cloudflare which fingerprints the TLS handshake (JA3/JA4) to distinguish browsers from automated clients. Standard Node.js fetch() and curl are blocked with 403 regardless of headers or cookies.
Solution: impit — a Rust-based (reqwest + napi-rs) drop-in fetch() replacement that uses Chrome's cipher suites and TLS configuration. Zero npm dependencies, prebuilt native binaries, Apache-2.0 license.
Node.js fetch() → JA3: Node.js fingerprint → Cloudflare 403
impit.fetch() → JA3: Chrome fingerprint → Cloudflare 200Architecture
tools/depop/
├── cli.js CLI entry point (args, routing, output formatting)
├── depop-api.js API client (impit fetch, all endpoints)
├── README.md This file
├── package.json @vibelyster/depop-cli
└── examples/ Payload templates (TODO)API Auth Flow
access_token cookie from browser
↓
Authorization: Bearer <token>
↓
webapi.depop.com/api/* (via impit for TLS fingerprint)
↓
userId auto-resolved from /api/v1/addresses/API Endpoints
| Method | Endpoint | Purpose |
|--------|----------|---------|
| GET | /api/v1/sellerOnboarding/sellerStatus/ | Auth check + seller status |
| GET | /api/v1/addresses/ | Shipping addresses (also returns userId) |
| GET | /api/v3/shop/{userId}/products/ | User's listings |
| GET | /api/v1/product/by-slug/{slug}/user/ | Product detail by slug |
| POST | /api/v2/pictures/ | Presigned S3 URL for image upload |
| POST | /api/v2/drafts/ | Create draft listing |
| GET | /api/v2/drafts/ | List drafts |
| PUT | /api/v2/drafts/{id}/ | Update draft |
| DELETE | /api/v1/drafts/{id}/ | Delete draft (v1, not v2) |
| PUT | /api/v2/products/{id}/ | Edit live product |
| DELETE | /api/v1/products/{id}/ | Delete live product (v1, not v2) |
Gotchas & Pitfalls
| Gotcha | Detail |
|--------|--------|
| Images must be square | Depop rejects non-square images. Crop before uploading. |
| Product lookup uses slugs, not IDs | /api/v2/products/{slug}/ — get slugs from depop listings |
| Cloudflare blocks Node.js fetch | Must use impit or equivalent Chrome TLS fingerprint tool |
| depop-UserId header is optional | Bearer token alone identifies the user |
| userId needed in listings URL | /api/v3/shop/{userId}/products/ — auto-resolved and cached |
| esbuild can't bundle impit | impit uses native binaries (napi-rs) — must be marked as external for bundling |
