# GET /jobs/{jobId} Source: https://docs.descripio.com/api-reference/jobs GET /api/v1/jobs/{jobId} Poll the status of a review scraping job # GET /api/v1/jobs/ Check the status of a review scraping job. ## Request **Headers**: | Header | Value | Required | | ------------- | --------------------- | -------- | | Authorization | `Bearer YOUR_API_KEY` | Yes | **Path Parameters**: | Parameter | Type | Description | | --------- | ------ | ----------------------------------- | | jobId | number | Job ID from `POST /reviews/refresh` | ## Response (200 OK) ```json theme={null} { "jobId": 2877, "asin": "B08N5WRWNW", "productTitle": "Example Product Title", "status": "completed", "createdAt": "2026-02-02T10:30:00.000Z", "updatedAt": "2026-02-02T10:31:05.000Z" } ``` | Field | Type | Description | | ------------ | ------ | ---------------------------------------------------------------- | | jobId | number | Job identifier | | asin | string | ASIN being processed | | productTitle | string | Product title (if available) | | status | string | `queued` \| `processing` \| `completed` \| `failed` \| `unknown` | | createdAt | string | ISO 8601 timestamp of job creation | | updatedAt | string | ISO 8601 timestamp of last status change | ## Job Statuses | Status | Description | | ---------- | ---------------------------------------------- | | queued | Job is waiting to be processed | | processing | Job is actively scraping reviews | | completed | Job finished successfully (reviews now cached) | | failed | Job failed | | unknown | Unexpected internal status (fallback value) | ## Errors | Status | Error | Cause | | ------ | ----------------------- | --------------------------------------------- | | 400 | `Invalid job ID` | jobId is not a valid number | | 401 | `Invalid API key` | Key doesn't exist or was revoked | | 404 | `Job not found` | Job doesn't exist or belongs to another store | | 500 | `Internal server error` | Temporary backend/service issue | ## Examples **cURL**: ```bash theme={null} curl https://app.descripio.com/api/v1/jobs/2877 \ -H "Authorization: Bearer dscr_abc123..." ``` **Python (with polling)**: ```python theme={null} import requests import time API_KEY = "dscr_abc123..." job_id = 2877 max_attempts = 24 # 2 minutes max for attempt in range(max_attempts): response = requests.get( f"https://app.descripio.com/api/v1/jobs/{job_id}", headers={"Authorization": f"Bearer {API_KEY}"} ) job = response.json() status = job["status"] if status == "completed": print(f"✅ Job complete! Fetching reviews...") break elif status == "failed": print(f"❌ Job failed") break else: print(f"⏳ Status: {status}, waiting...") time.sleep(5) else: print("⚠️ Timeout: Job took longer than expected") ``` **JavaScript (with polling)**: ```javascript theme={null} async function pollJob(jobId, apiKey) { const maxAttempts = 24; for (let attempt = 0; attempt < maxAttempts; attempt++) { const response = await fetch( `https://app.descripio.com/api/v1/jobs/${jobId}`, { headers: { 'Authorization': `Bearer ${apiKey}` } } ); const job = await response.json(); if (job.status === 'completed') { console.log('Job complete!'); return job; } else if (job.status === 'failed') { throw new Error('Job failed'); } console.log(`Status: ${job.status}, waiting...`); await new Promise(resolve => setTimeout(resolve, 5000)); } throw new Error('Timeout'); } ``` ## Best Practices * **Poll interval**: Start with 5 seconds, use exponential backoff if needed * **Max timeout**: Set a reasonable limit (2-3 minutes) to avoid infinite loops * **Error handling**: Always check for `failed` status and handle gracefully # API Overview Source: https://docs.descripio.com/api-reference/overview Understanding the async architecture # API Overview Descripio API uses an **async job-based architecture** to handle Amazon's rate limits gracefully. ## Architecture ``` POST /reviews/refresh → Job Created (queued) ↓ Poll GET /jobs/:id → Status: queued → processing → completed ↓ GET /reviews?asin=... → Cached reviews returned ``` **Why async?** Scraping Amazon reviews takes 30-90 seconds. Instead of blocking your request, we: 1. Accept your job immediately (HTTP 202) 2. Process it in the background 3. Cache results for instant retrieval ## Base URL ``` https://app.descripio.com/api/v1 ``` ## Endpoints | Endpoint | Method | Description | | ------------------ | ------ | ----------------------- | | `/reviews/refresh` | POST | Trigger review scraping | | `/jobs/:jobId` | GET | Check job status | | `/reviews` | GET | Fetch cached reviews | ## Rate Limits | Plan | Refresh Jobs/Month | Rate Limit | Concurrent Jobs | | -------- | ------------------ | ---------- | --------------- | | Free | 100 | 1/min | 1 | | Starter | 1,000 | 10/min | 2 | | Pro | 5,000 | 50/min | 5 | | Business | 30,000 | 200/min | 10 | > **Important**: Only POST `/reviews/refresh` counts toward your quota. GET requests are free and unlimited. ## HTTP Status Codes | Code | Meaning | | ---- | ----------------------------------------- | | 200 | Success | | 202 | Job accepted (async processing started) | | 400 | Bad request (invalid parameters) | | 401 | Unauthorized (invalid or missing API key) | | 402 | Payment required (monthly quota exceeded) | | 403 | Forbidden (marketplace not authorized) | | 404 | Not found (job or cached data not found) | | 429 | Rate limit exceeded | | 500 | Internal server error | ## Content Type All requests and responses use JSON: ``` Content-Type: application/json ``` ## Versioning The API is versioned via URL path: ``` /api/v1/... ``` Breaking changes will be introduced in new versions (e.g., `/api/v2/`). # POST /reviews/refresh Source: https://docs.descripio.com/api-reference/refresh POST /api/v1/reviews/refresh Trigger a review scraping job for an ASIN # POST /api/v1/reviews/refresh Trigger a review scraping job for an Amazon product. ## Request **Headers**: | Header | Value | Required | | ------------- | --------------------- | -------- | | Authorization | `Bearer YOUR_API_KEY` | Yes | | Content-Type | `application/json` | Yes | **Body**: ```json theme={null} { "asin": "B08N5WRWNW", "marketplace": "amazon.de" } ``` | Field | Type | Required | Description | | ----------- | ------ | -------- | ------------------------------------------------------- | | asin | string | Yes | Amazon ASIN (10 alphanumeric characters) | | marketplace | string | No | Amazon marketplace (defaults to `amazon.de` if omitted) | ## Response (202 Accepted) ```json theme={null} { "jobId": 2877, "asin": "B08N5WRWNW", "status": "queued", "message": "Review refresh job created successfully", "estimatedCompletionSeconds": 60 } ``` | Field | Type | Description | | -------------------------- | ------ | -------------------------------------- | | jobId | number | Unique job ID (use to poll status) | | asin | string | The ASIN being processed | | status | string | Always `queued` for new jobs | | message | string | Human-readable confirmation | | estimatedCompletionSeconds | number | Typical completion time (\~60 seconds) | ## Errors | Status | Error | Cause | Solution | | ------ | ---------------------------- | ------------------------------------- | -------------------------------- | | 400 | `Invalid ASIN format` | ASIN not 10 alphanumeric characters | Verify ASIN format | | 401 | `Invalid API key` | Key doesn't exist or was revoked | Check your API key | | 402 | `Monthly quota exceeded` | Used all refresh jobs for the month | Wait for reset or upgrade plan | | 403 | `Marketplace not authorized` | Store doesn't have marketplace access | Add marketplace in dashboard | | 429 | `Rate limit exceeded` | Too many requests per minute | Wait 60 seconds | | 429 | `Too many concurrent jobs` | Concurrent job limit reached | Wait for a job to complete | | 429 | `ASIN cooldown active` | ASIN was refreshed recently | Wait for cooldown (24h for Free) | | 500 | `service_error` | Temporary backend/service issue | Retry after a short delay | ## Examples **cURL**: ```bash theme={null} curl -X POST https://app.descripio.com/api/v1/reviews/refresh \ -H "Authorization: Bearer dscr_abc123..." \ -H "Content-Type: application/json" \ -d '{ "asin": "B08N5WRWNW", "marketplace": "amazon.de" }' ``` **Python**: ```python theme={null} import requests response = requests.post( "https://app.descripio.com/api/v1/reviews/refresh", headers={"Authorization": "Bearer dscr_abc123..."}, json={ "asin": "B08N5WRWNW", "marketplace": "amazon.de" } ) if response.status_code == 202: job = response.json() print(f"Job ID: {job['jobId']}") else: print(f"Error: {response.json()}") ``` **JavaScript**: ```javascript theme={null} const response = await fetch( 'https://app.descripio.com/api/v1/reviews/refresh', { method: 'POST', headers: { 'Authorization': 'Bearer dscr_abc123...', 'Content-Type': 'application/json' }, body: JSON.stringify({ asin: 'B08N5WRWNW', marketplace: 'amazon.de' }) } ); const job = await response.json(); console.log(`Job ID: ${job.jobId}`); ``` ## Notes * **Cooldown**: Each ASIN can only be refreshed once per 24 hours (Free tier). Paid plans have shorter cooldowns. * **Caching**: Results are cached after job completion. Use `GET /reviews` to fetch without triggering a new job. * **Concurrent jobs**: The number of jobs you can run simultaneously depends on your plan. # GET /reviews Source: https://docs.descripio.com/api-reference/reviews GET /api/v1/reviews Fetch cached review data for an ASIN # GET /api/v1/reviews Fetch cached review data for an Amazon product. > **Free & Unlimited**: This endpoint doesn't count toward your quota. Only refresh jobs are metered. ## Request **Headers**: | Header | Value | Required | | ------------- | --------------------- | -------- | | Authorization | `Bearer YOUR_API_KEY` | Yes | **Query Parameters**: | Parameter | Type | Required | Description | | --------- | ------ | -------- | ---------------------------- | | asin | string | Yes | Amazon ASIN to fetch reviews | ## Response (200 OK) ```json theme={null} { "asin": "B08N5WRWNW", "title": "Example Product Title", "imageUrl": "https://m.media-amazon.com/images/I/...", "marketplace": "amazon.de", "reviewCount": 156, "reviews": [ { "id": 12345, "amazonReviewId": "R1ABC123DEF", "title": "Great product!", "content": "I've been using this for a month and it's exactly what I needed...", "date": "2026-01-15T00:00:00.000Z", "isTopReview": true, "country": "DE", "scrapedAt": "2026-02-02T10:31:00.000Z" } ], "lastScraped": "2026-02-02T10:31:00.000Z", "cached": true } ``` | Field | Type | Description | | ----------- | ------- | ---------------------------------- | | asin | string | Amazon ASIN | | title | string | Product title | | imageUrl | string | Product image URL | | marketplace | string | Amazon marketplace | | reviewCount | number | Total number of cached reviews | | reviews | array | Array of review objects | | lastScraped | string | ISO 8601 timestamp of last refresh | | cached | boolean | Whether data is from cache | ### Review Object | Field | Type | Description | | -------------- | ------- | ------------------------------ | | id | number | Internal review ID | | amazonReviewId | string | Amazon's review ID | | title | string | Review headline | | content | string | Full review text | | date | string | Review date (ISO 8601) | | isTopReview | boolean | Whether this is a "Top Review" | | country | string | Reviewer's country code | | scrapedAt | string | When review was scraped | ## Response (404 Not Found) When no cached data exists for an ASIN: ```json theme={null} { "asin": "B08N5WRWNW", "reviews": [], "cached": false, "message": "No cached data. Create a refresh job first." } ``` ## Errors | Status | Error | Cause | | ------ | ----------------------- | --------------------------------- | | 400 | `Missing asin` | asin query parameter not provided | | 401 | `Invalid API key` | Key doesn't exist or was revoked | | 404 | `No cached data` | ASIN hasn't been refreshed yet | | 500 | `Internal server error` | Temporary backend/service issue | ## Examples **cURL**: ```bash theme={null} curl "https://app.descripio.com/api/v1/reviews?asin=B08N5WRWNW" \ -H "Authorization: Bearer dscr_abc123..." ``` **Python**: ```python theme={null} import requests response = requests.get( "https://app.descripio.com/api/v1/reviews", params={"asin": "B08N5WRWNW"}, headers={"Authorization": "Bearer dscr_abc123..."} ) data = response.json() if data.get("cached"): print(f"Found {data['reviewCount']} reviews") for review in data["reviews"][:5]: print(f"- {review['title']}") else: print("No cached data. Run a refresh job first.") ``` **JavaScript**: ```javascript theme={null} const response = await fetch( 'https://app.descripio.com/api/v1/reviews?asin=B08N5WRWNW', { headers: { 'Authorization': 'Bearer dscr_abc123...' } } ); const data = await response.json(); if (data.cached) { console.log(`Found ${data.reviewCount} reviews`); data.reviews.slice(0, 5).forEach(r => console.log(`- ${r.title}`)); } else { console.log('No cached data. Run a refresh job first.'); } ``` ## Notes * **Caching**: Data persists until the next refresh job for that ASIN * **Full Review Text**: Get up to 200 reviews per ASIN (10 pages recent + 10 pages helpful). * **Free**: This endpoint is free and unlimited - only refresh jobs count toward quota # Authentication Source: https://docs.descripio.com/authentication How to create and use API keys # Authentication All Descripio API requests require authentication via Bearer token. ## Creating an API Key 1. Log in to [app.descripio.com](https://app.descripio.com) 2. Navigate to [**Developer → API Keys**](https://app.descripio.com/developer/api-keys) 3. Click **"Create API Key"** 4. Choose your plan (Free tier available) 5. Copy the key **immediately** (shown only once) > ⚠️ **Store your API key securely**. It won't be shown again after creation. ## Using Your API Key Include your API key in the `Authorization` header with every request: ```bash theme={null} curl https://app.descripio.com/api/v1/reviews?asin=B08N5WRWNW \ -H "Authorization: Bearer dscr_abc123..." ``` ## Security Best Practices ### Never commit keys to Git Use environment variables: ```bash theme={null} export DESCRIPIO_API_KEY="dscr_abc123..." curl -H "Authorization: Bearer $DESCRIPIO_API_KEY" ... ``` ### Rotate keys if compromised Revoke the old key and create a new one immediately via the dashboard. ### Use separate keys per environment Create different keys for development, staging, and production. ### Server-side only Never expose API keys in client-side code (browsers, mobile apps). Always make API calls from your backend. ## Key Format All Descripio API keys follow this format: ``` dscr_XXXXXXXXXXXXXXXXXXXXXXXXXXXX ``` * Prefix: `dscr_` * Length: 32 characters after prefix * Characters: alphanumeric + hyphens ## Managing API Keys From the [API Keys dashboard](https://app.descripio.com/developer/api-keys) you can: * **View keys**: See all your active keys with usage stats * **Monitor usage**: Track requests used vs. monthly quota * **Revoke keys**: Immediately disable a key (cannot be undone) ## Rate Limits by Plan | Plan | Refresh Jobs/Month | Rate Limit | Concurrent Jobs | | -------- | ------------------ | ---------- | --------------- | | Free | 100 | 1/min | 1 | | Starter | 1,000 | 10/min | 2 | | Pro | 5,000 | 50/min | 5 | | Business | 30,000 | 200/min | 10 | > **Note**: GET requests (fetching cached reviews) are unlimited and free. [View full pricing →](https://descripio.com/en/api/amazon-review-scraper) # Error Handling Source: https://docs.descripio.com/guides/error-handling Handle API errors gracefully # Error Handling All Descripio API errors return a JSON response with an `error` field. ## Error Response Format ```json theme={null} { "error": "Human-readable error message", "limit": 10, "used": 10 } ``` Additional fields may be included depending on the error type. ## Common Errors ### 401 Unauthorized **Cause**: Invalid or revoked API key ```json theme={null} { "error": "Invalid API key" } ``` **Solution**: 1. Verify your API key is correct (starts with `dscr_`) 2. Check if key was revoked in the [dashboard](https://app.descripio.com/developer/api-keys) 3. Create a new key if needed ### 402 Payment Required **Cause**: Monthly quota exceeded ```json theme={null} { "error": "Monthly quota exceeded", "used": 10, "limit": 10 } ``` **Solution**: 1. Wait for quota reset (1st of each month) 2. Upgrade to a higher plan ### 403 Forbidden **Cause**: Marketplace not authorized for your account ```json theme={null} { "error": "Marketplace not authorized for this store", "hint": "Configure marketplace access in your account settings" } ``` **Solution**: 1. Go to Dashboard → Settings → Marketplaces 2. Add the marketplace you're trying to access 3. Retry the request ### 429 Rate Limit Exceeded **Cause**: Too many requests or concurrent jobs ```json theme={null} { "error": "Rate limit exceeded", "limit": 1 } ``` **Solution**: Implement retry with backoff: ```python theme={null} import time import requests def api_call_with_retry(url, headers, max_retries=3): for attempt in range(max_retries): response = requests.get(url, headers=headers) if response.status_code == 429: wait_time = (2 ** attempt) * 10 # 10s, 20s, 40s print(f"Rate limited. Waiting {wait_time}s...") time.sleep(wait_time) continue return response raise Exception("Max retries exceeded") ``` ### 429 ASIN Cooldown **Cause**: ASIN was refreshed recently ```json theme={null} { "error": "ASIN cooldown active", "cooldownHours": 24 } ``` **Solution**: Wait for cooldown period to expire, or use cached data via `GET /reviews`. ## Best Practices ### 1. Always check status codes ```python theme={null} response = requests.post(url, headers=headers, json=data) if response.status_code == 202: job = response.json() # Success - process job elif response.status_code == 429: # Rate limited - wait and retry elif response.status_code == 402: # Quota exceeded - alert user else: # Log unexpected error print(f"Error {response.status_code}: {response.json()}") ``` ### 2. Implement exponential backoff ```javascript theme={null} async function fetchWithBackoff(url, options, maxRetries = 3) { for (let attempt = 0; attempt < maxRetries; attempt++) { const response = await fetch(url, options); if (response.status === 429) { const waitTime = Math.pow(2, attempt) * 10000; // 10s, 20s, 40s await new Promise(r => setTimeout(r, waitTime)); continue; } return response; } throw new Error('Max retries exceeded'); } ``` ### 3. Set polling timeout Never poll forever. Always set a maximum timeout: ```python theme={null} max_attempts = 24 # 2 minutes at 5s intervals timeout_seconds = 120 for attempt in range(max_attempts): # ... poll job status time.sleep(5) else: raise TimeoutError("Job did not complete in time") ``` ### 4. Log errors for debugging Include relevant context when logging errors: ```python theme={null} import logging logging.error( "API Error", extra={ "status_code": response.status_code, "error": response.json().get("error"), "asin": asin, "timestamp": datetime.now().isoformat() } ) ``` ### 5. Handle network errors ```python theme={null} import requests from requests.exceptions import Timeout, ConnectionError try: response = requests.post(url, headers=headers, json=data, timeout=30) except Timeout: print("Request timed out - check your connection") except ConnectionError: print("Connection failed - check if service is available") ``` ## Support If you encounter persistent errors: * **Email**: [support@descripio.com](mailto:support@descripio.com) * **Include**: Job ID, timestamp, error message, request details # Introduction Source: https://docs.descripio.com/introduction Amazon Review Data API - Built for Developers # Welcome to Descripio API Access rich Amazon product review data through a simple, async-first REST API. ## Why Descripio API? **Async-First Architecture**\ No timeouts. Trigger a job, poll for completion, read cached results. **Full Review Text**\ Get up to 200 reviews per ASIN (10 pages recent + 10 pages helpful). **Predictable Pricing**\ Quota-based plans. No hidden overage charges. **Battle-Tested Infrastructure**\ Built on Descripio's production scraping infrastructure serving 1000+ sellers. ## Quick Links * [Quick Start →](/quickstart) * [API Reference →](/api-reference/overview) * [View Pricing →](https://app.descripio.com/pricing) * [Get API Key →](https://app.descripio.com/developer/api-keys) * [Download Postman Collection →](https://app.descripio.com/descripio-reviews-api.postman_collection.json) ## Base URL All API requests use the following base URL: ``` https://app.descripio.com/api/v1 ``` ## Authentication All endpoints require a Bearer token in the `Authorization` header: ```bash theme={null} Authorization: Bearer dscr_abc123... ``` [Learn more about authentication →](/authentication) # 5-Minute Quick Start Source: https://docs.descripio.com/quickstart Get your first API call working # Quick Start **Prefer Postman?** Skip the manual steps — import our pre-configured collection and start making real API calls in under a minute. [Download Postman Collection →](https://app.descripio.com/descripio-reviews-api.postman_collection.json) Import the file into Postman: **File → Import**, paste the URL or drop the downloaded file. Your `baseUrl`, `asin`, `marketplace` variables are pre-filled. Just set your `apiKey`. ## Step 1: Get Your API Key 1. Sign up at [app.descripio.com](https://app.descripio.com) 2. Navigate to [**Developer → API Keys**](https://app.descripio.com/developer/api-keys) 3. Click **"Create API Key"** 4. Copy your key (starts with `dscr_`) > ⚠️ **Save your key securely**. It won't be shown again after creation. ## Step 2: Trigger a Review Refresh Replace `YOUR_API_KEY` with your actual key: ```bash theme={null} curl -X POST https://app.descripio.com/api/v1/reviews/refresh \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "asin": "B08N5WRWNW", "marketplace": "amazon.de" }' ``` **Response** (HTTP 202 Accepted): ```json theme={null} { "jobId": 2877, "asin": "B08N5WRWNW", "status": "queued", "message": "Review refresh job created successfully", "estimatedCompletionSeconds": 60 } ``` ## Step 3: Poll Job Status (With Timeout) **Python (Recommended)**: ```python theme={null} import requests import time API_KEY = "YOUR_API_KEY" job_id = 2877 max_attempts = 24 # 2 minutes max (5s interval) attempt = 0 while attempt < max_attempts: response = requests.get( f"https://app.descripio.com/api/v1/jobs/{job_id}", headers={"Authorization": f"Bearer {API_KEY}"} ) job = response.json() status = job.get("status") if status == "completed": print("Job complete!") break elif status == "failed": print("Job failed!") break elif status == "unknown": print("Unexpected job status. Please retry.") break print(f"Status: {status}, waiting...") time.sleep(5) # Wait 5 seconds before next poll attempt += 1 if attempt >= max_attempts: print("Timeout: Job took longer than expected") ``` Possible job statuses: `queued`, `processing`, `completed`, `failed`, `unknown`. **cURL (Manual polling)**: ```bash theme={null} curl https://app.descripio.com/api/v1/jobs/2877 \ -H "Authorization: Bearer YOUR_API_KEY" ``` **Response** (when complete): ```json theme={null} { "jobId": 2877, "asin": "B08N5WRWNW", "productTitle": "Example Product Title", "status": "completed", "createdAt": "2026-02-02T10:30:00.000Z", "updatedAt": "2026-02-02T10:31:05.000Z" } ``` ## Step 4: Fetch Reviews ```bash theme={null} curl "https://app.descripio.com/api/v1/reviews?asin=B08N5WRWNW" \ -H "Authorization: Bearer YOUR_API_KEY" ``` **Response**: ```json theme={null} { "asin": "B08N5WRWNW", "title": "Example Product Title", "imageUrl": "https://m.media-amazon.com/images/...", "marketplace": "amazon.de", "reviewCount": 156, "reviews": [ { "id": 12345, "amazonReviewId": "R1ABC123DEF", "title": "Great product!", "content": "I've been using this for a month and...", "date": "2026-01-15T00:00:00.000Z", "isTopReview": true, "country": "DE", "scrapedAt": "2026-02-02T10:31:00.000Z" } ], "lastScraped": "2026-02-02T10:31:00.000Z", "cached": true } ``` If the ASIN has no cached data yet, the endpoint returns **404 Not Found**: ```json theme={null} { "asin": "B08N5WRWNW", "reviews": [], "cached": false, "message": "No cached data. Create a refresh job first." } ``` ## Next Steps * [Authentication Guide →](/authentication) * [Error Handling →](/guides/error-handling) * [Full API Reference →](/api-reference/overview)