From 18fabc5437908799dcfedabb1e6719d3b4cb5fb3 Mon Sep 17 00:00:00 2001 From: kasparasizi1 <132673909+kasparasizi1@users.noreply.github.com> Date: Sun, 12 Jul 2026 12:10:41 +0300 Subject: [PATCH] feat(depop): add Depop scraper client (v0.19.0) DepopClient with search/getProduct/getUser/getUserProducts/listMarkets over /v1/depop/*. Mirrors the Redfin/LoopNet modules. --- package.json | 2 +- src/client.ts | 5 ++ src/depop/client.ts | 156 ++++++++++++++++++++++++++++++++++++++++++++ src/depop/index.ts | 26 ++++++++ src/depop/types.ts | 156 ++++++++++++++++++++++++++++++++++++++++++++ src/index.ts | 3 + 6 files changed, 347 insertions(+), 1 deletion(-) create mode 100644 src/depop/client.ts create mode 100644 src/depop/index.ts create mode 100644 src/depop/types.ts diff --git a/package.json b/package.json index e8043c8..e7e546f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "scrapebadger", - "version": "0.18.0", + "version": "0.19.0", "description": "Official Node.js SDK for ScrapeBadger - Async web scraping APIs for Twitter, Google, Vinted, Reddit, and more", "type": "module", "main": "./dist/index.js", diff --git a/src/client.ts b/src/client.ts index 37ff13b..c89f81f 100644 --- a/src/client.ts +++ b/src/client.ts @@ -22,6 +22,7 @@ import { LeboncoinClient } from "./leboncoin/client.js"; import { ZillowClient } from "./zillow/client.js"; import { ImmobiliareClient } from "./immobiliare/client.js"; import { LoopNetClient } from "./loopnet/client.js"; +import { DepopClient } from "./depop/client.js"; /** * ScrapeBadger API client. @@ -106,6 +107,9 @@ export class ScrapeBadger { /** LoopNet scraper API client — commercial real estate across US/CA/UK/FR/ES (search, listing, broker, markets, property types) */ readonly loopnet: LoopNetClient; + /** Depop scraper API client — search, product, user, user products, markets (www.depop.com, 10 markets) */ + readonly depop: DepopClient; + /** * Create a new ScrapeBadger client. * @@ -161,5 +165,6 @@ export class ScrapeBadger { this.zillow = new ZillowClient(this.baseClient); this.immobiliare = new ImmobiliareClient(this.baseClient); this.loopnet = new LoopNetClient(this.baseClient); + this.depop = new DepopClient(this.baseClient); } } diff --git a/src/depop/client.ts b/src/depop/client.ts new file mode 100644 index 0000000..9214395 --- /dev/null +++ b/src/depop/client.ts @@ -0,0 +1,156 @@ +/** + * Depop API client. + * + * Depop endpoints: search (product search), getProduct (full single-product + * detail, by slug), getUser (shop / seller profile, by username), + * getUserProducts (a seller's products), and listMarkets. Single host + * (www.depop.com) localised by a `market` param → country + currency. + * + * Data is browser-rendered (cloakbrowser), so each call takes a few seconds + * to return. + */ + +import type { BaseClient } from "../internal/client.js"; +import type { + DepopSearchParams, + DepopProductParams, + DepopUserParams, + DepopUserProductsParams, + DepopSearchResponse, + DepopProductDetail, + DepopShopProfile, + DepopUserProductsResponse, + DepopMarketsResponse, +} from "./types.js"; + +/** + * Client for all Depop API operations. + * + * @example + * ```typescript + * const client = new ScrapeBadger({ apiKey: "key" }); + * + * // Search products + * const results = await client.depop.search("vintage denim jacket", { market: "gb" }); + * for (const product of results.products) { + * console.log(`${product.brand} — ${product.price} ${product.currency}`); + * } + * + * // Full single-product detail + * const detail = await client.depop.getProduct("some-product-slug"); + * console.log(detail.title); + * + * // Shop profile + * const shop = await client.depop.getUser("someseller"); + * + * // A seller's products + * const items = await client.depop.getUserProducts("someseller"); + * + * // Supported markets + * const markets = await client.depop.listMarkets(); + * ``` + */ +export class DepopClient { + private readonly client: BaseClient; + + constructor(client: BaseClient) { + this.client = client; + } + + /** + * Search Depop for products. + * + * Costs 10 credits. + * + * @param query - Search keywords (required). + * @param options - Optional parameters (market, perPage, page, price/brand/ + * size/colour/condition/gender filters, sort). + * @returns Search response with matching product cards and pagination meta. + * @throws AuthenticationError - If the API key is invalid. + * @throws ValidationError - If the parameters are invalid. + */ + async search(query: string, options: DepopSearchParams = {}): Promise { + return this.client.request("/v1/depop/search", { + params: { + query, + market: options.market ?? "us", + per_page: options.perPage, + page: options.page, + price_min: options.priceMin, + price_max: options.priceMax, + brands: options.brands, + sizes: options.sizes, + colours: options.colours, + conditions: options.conditions, + gender: options.gender, + sort: options.sort, + }, + }); + } + + /** + * Get a single Depop product's full detail by slug. + * + * Costs 10 credits. + * + * @param slug - The Depop product slug. + * @param options - Optional parameters (market). + * @returns Full product detail. + * @throws NotFoundError - If the product doesn't exist. + */ + async getProduct(slug: string, options: DepopProductParams = {}): Promise { + return this.client.request(`/v1/depop/products/${slug}`, { + params: { market: options.market ?? "us" }, + }); + } + + /** + * Get a Depop shop / seller profile by username. + * + * Costs 10 credits. + * + * @param username - The Depop seller username. + * @param options - Optional parameters (market). + * @returns Shop profile. + * @throws NotFoundError - If the user doesn't exist. + */ + async getUser(username: string, options: DepopUserParams = {}): Promise { + return this.client.request(`/v1/depop/users/${username}`, { + params: { market: options.market ?? "us" }, + }); + } + + /** + * Get a Depop seller's products. + * + * Costs 10 credits. + * + * @param username - The Depop seller username. + * @param options - Optional parameters (market, perPage, page). + * @returns User products response with product cards and pagination meta. + * @throws NotFoundError - If the user doesn't exist. + */ + async getUserProducts( + username: string, + options: DepopUserProductsParams = {} + ): Promise { + return this.client.request(`/v1/depop/users/${username}/products`, { + params: { + market: options.market ?? "us", + per_page: options.perPage, + page: options.page, + }, + }); + } + + /** + * Get all supported Depop markets. + * + * Free — this endpoint costs no credits. + * + * @returns Markets response with all supported markets. + */ + async listMarkets(): Promise { + return this.client.request("/v1/depop/markets"); + } +} diff --git a/src/depop/index.ts b/src/depop/index.ts new file mode 100644 index 0000000..b29018c --- /dev/null +++ b/src/depop/index.ts @@ -0,0 +1,26 @@ +/** + * Depop API module. + * + * @module depop + */ + +export { DepopClient } from "./client.js"; + +// Export all types +export type { + // Shared + DepopCard, + SearchMeta as DepopSearchMeta, + DepopMarket, + // Response envelopes + DepopSearchResponse, + DepopProductDetail, + DepopShopProfile, + DepopUserProductsResponse, + DepopMarketsResponse, + // Request params + DepopSearchParams, + DepopProductParams, + DepopUserParams, + DepopUserProductsParams, +} from "./types.js"; diff --git a/src/depop/types.ts b/src/depop/types.ts new file mode 100644 index 0000000..f8c9264 --- /dev/null +++ b/src/depop/types.ts @@ -0,0 +1,156 @@ +/** + * TypeScript types for Depop API responses. + * + * These interfaces mirror the backend `depop_scraper` response schema + * field-for-field. Keys are snake_case exactly as the backend serialises + * them (`seller_username`, `original_price`, `is_sold`); optional / nullable + * backend fields are typed as `Type | null` or left `?`-optional. + * + * Depop is a single-host target (www.depop.com) localised by a `market` + * param → country + currency. Data is browser-rendered, so responses carry + * the raw parsed cards/detail rather than an underlying JSON API shape. + */ + +// ============================================================================= +// Shared +// ============================================================================= + +/** One Depop product card (search / user-products result element). */ +export interface DepopCard { + /** Product slug (last URL segment). */ + slug: string; + /** Full product URL. */ + url: string; + /** Seller's Depop username. */ + seller_username?: string; + /** Brand name. */ + brand?: string; + /** Item size label. */ + size?: string; + /** Current price (as displayed). */ + price?: string; + /** Original price before markdown. */ + original_price?: string; + /** Currency code / symbol for the price. */ + currency?: string; + /** Primary product image URL. */ + image?: string; + /** Whether the item is marked sold. */ + is_sold: boolean; +} + +/** Result-set metadata for a paginated listing. */ +export interface SearchMeta { + result_count: number; + page: number; + has_more: boolean; +} + +/** A supported Depop market (for /markets). */ +export interface DepopMarket { + code: string; + country_code: string; + currency: string; + name: string; +} + +// ============================================================================= +// Response Envelopes +// ============================================================================= + +export interface DepopSearchResponse { + products: DepopCard[]; + meta: SearchMeta; + market: string; + query: string; +} + +/** Full Depop product detail. */ +export interface DepopProductDetail { + id?: number; + slug: string; + title?: string | null; + description?: string | null; + brand?: string | null; + condition?: string | null; + price?: string | null; + currency?: string | null; + availability?: string | null; + seller_username?: string | null; + images: string[]; + url: string; +} + +/** A Depop shop / seller profile. */ +export interface DepopShopProfile { + username: string; + name?: string | null; + description?: string | null; + rating_value?: string | null; + rating_count?: number | null; + follower_count?: number | null; + url: string; +} + +export interface DepopUserProductsResponse { + username: string; + products: DepopCard[]; + meta: SearchMeta; + market: string; +} + +export interface DepopMarketsResponse { + markets: DepopMarket[]; +} + +// ============================================================================= +// Request Parameter Types +// ============================================================================= + +/** Options for the /search endpoint. */ +export interface DepopSearchParams { + /** Market code (default: "us"). Localises country + currency. */ + market?: string; + /** Results per page (1-24, default: 24). */ + perPage?: number; + /** Page number (default: 1). */ + page?: number; + /** Minimum price filter. */ + priceMin?: number; + /** Maximum price filter. */ + priceMax?: number; + /** Brand filter(s). */ + brands?: string; + /** Size filter(s). */ + sizes?: string; + /** Colour filter(s). */ + colours?: string; + /** Condition filter(s). */ + conditions?: string; + /** Gender filter. */ + gender?: string; + /** Sort order. */ + sort?: string; +} + +/** Options for the /products/{slug} endpoint. */ +export interface DepopProductParams { + /** Market code (default: "us"). */ + market?: string; +} + +/** Options for the /users/{username} endpoint. */ +export interface DepopUserParams { + /** Market code (default: "us"). */ + market?: string; +} + +/** Options for the /users/{username}/products endpoint. */ +export interface DepopUserProductsParams { + /** Market code (default: "us"). */ + market?: string; + /** Results per page (1-24, default: 24). */ + perPage?: number; + /** Page number (default: 1). */ + page?: number; +} diff --git a/src/index.ts b/src/index.ts index 7850f69..a65c8e6 100644 --- a/src/index.ts +++ b/src/index.ts @@ -99,6 +99,9 @@ export * from "./immobiliare/index.js"; // Re-export LoopNet module — LoopNetClient + LoopNet*-prefixed types export * from "./loopnet/index.js"; +// Re-export Depop module — DepopClient + Depop*-prefixed types +export * from "./depop/index.js"; + // Re-export Leboncoin module — LeboncoinClient + Leboncoin*-prefixed sub-clients and types export * from "./leboncoin/index.js";