API Reference

Complete API documentation with request/response examples and error handling.

Authentication & Security

InjectAPI uses API keys for authentication. All API requests must include your API key in the X-API-Key header.

API Key Format

API keys follow the format: ik_ followed by 32 alphanumeric characters.

# Example API key
ik_1a2b3c4d5e6f7g8h9i0j1k2l3m4n5o6p

You can generate and manage your API keys from the Dashboard.

Using Your API Key

Include your API key in the X-API-Key header with every request:

curl -X POST https://api.injectapi.com/api/extract \
  -H "X-API-Key: ik_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com", "mode": "product"}'

Security Best Practices

1. Never Hardcode API Keys

Always use environment variables to store your API keys:

// ✅ Good - Use environment variables
const apiKey = process.env.INJECTAPI_KEY;

// ❌ Bad - Never commit API keys to version control
const apiKey = 'ik_1a2b3c4d5e6f7g8h9i0j1k2l3m4n5o6p';

2. Use Different Keys for Different Environments

Generate separate API keys for development, staging, and production environments. This allows you to:

  • Isolate usage and costs per environment
  • Rotate production keys without affecting development
  • Revoke compromised keys without disrupting other environments

3. Rotate Keys Regularly

Regenerate your API keys periodically (e.g., every 90 days) to minimize the impact of potential key exposure. InjectAPI allows you to have multiple active keys during rotation.

4. Restrict Key Usage

Monitor your API key usage in the dashboard and set up alerts for:

  • Unusual traffic patterns
  • Unexpected geographic locations
  • Quota threshold warnings (80%, 90%, 100%)

5. Server-Side Only

⚠️ Important: Never expose your API keys in client-side code (JavaScript running in the browser). Always make API requests from your backend server.

// ✅ Good - Backend (Node.js server)
app.post('/api/scrape', async (req, res) => {
  const response = await fetch('https://api.injectapi.com/api/extract', {
    headers: { 'X-API-Key': process.env.INJECTAPI_KEY }
  });
  res.json(await response.json());
});

// ❌ Bad - Frontend (exposed in browser)
fetch('https://api.injectapi.com/api/extract', {
  headers: { 'X-API-Key': 'ik_exposed_key_visible_to_users' } // DO NOT DO THIS!
});

6. Revoke Compromised Keys Immediately

If you suspect your API key has been compromised, revoke it immediately from the dashboard and generate a new one.


POST /api/compare-prices

Recommended

Compare product prices across multiple retailers with a single API call. Ultra-fast with Redis caching.

POST https://api.injectapi.com/api/compare-prices

Request Body

ParameterTypeRequiredDefaultDescription
productstringYes-Product search query
retailersstring[]No["target", "ebay"]Retailers to search
maxResultsnumberNo10Max products per retailer (1-100)
offsetnumberNo0Skip first N products (for incremental loading)
includeAIAnalysisbooleanNofalseInclude AI-generated summary
formatstringNo"grouped""grouped" or "flat" (use "flat" for per-product cache metadata)
cacheCategorystringNo"general"Cache namespace ("general", "comparison", "wishlist", or custom)

Incremental Loading Pattern

Use the offset parameter to load products in batches for better UX:

// Load first 10 products (3-4s)
const batch1 = await fetch('/api/compare-prices', {
  method: 'POST',
  body: JSON.stringify({
    product: 'wireless headphones',
    maxResults: 10,
    offset: 0
  })
});

// Load next 10 products (3-4s)
const batch2 = await fetch('/api/compare-prices', {
  method: 'POST',
  body: JSON.stringify({
    product: 'wireless headphones',
    maxResults: 10,
    offset: 10  // Skip first 10
  })
});

Response Format

{
  "success": true,
  "query": "nintendo switch",
  "results": [
    {
      "title": "Nintendo Switch OLED Model",
      "price": 349.99,
      "currency": "USD",
      "url": "https://www.target.com/...",
      "image": "https://...",
      "availability": "In Stock",
      "rating": 4.8,
      "review_count": 1234,
      "retailer": "target",
      "product_id": "A-12345678",
      "normalized_title": "Nintendo Switch OLED Model"
    }
  ],
  "metadata": {
    "total_products": 8,
    "retailers_searched": 2,
    "retailers_successful": ["target", "ebay"],
    "retailers_failed": [],
    "response_time_ms": 3542,
    "from_cache": false,
    "cache_age_seconds": 0,
    "cache_fresh": true
  }
}

Cache Headers

Every price comparison response includes cache status headers:

HeaderValuesDescription
X-CacheHIT | MISSWhether result was served from cache
X-Cache-AgenumberAge of cached result in seconds
X-Cache-Freshtrue | falseWhether cache is within freshness window (5 min)

Examples

Target Only (Fastest - 3-4s)

curl -X POST https://api.injectapi.com/api/compare-prices \
  -H "Content-Type: application/json" \
  -H "X-API-Key: YOUR_API_KEY" \
  -d '{
    "product": "ps5",
    "retailers": ["target"]
  }'

All Retailers with Flat Format

const response = await fetch('https://api.injectapi.com/api/compare-prices', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-API-Key': 'YOUR_API_KEY'
  },
  body: JSON.stringify({
    product: 'nintendo switch',
    retailers: ['target', 'ebay'],
    format: 'flat'
  })
});

const data = await response.json();

// Check cache status
const cacheStatus = response.headers.get('X-Cache'); // 'HIT' or 'MISS'
const cacheAge = response.headers.get('X-Cache-Age'); // seconds

if (data.metadata.from_cache) {
  console.log(`Cached result (${data.metadata.cache_age_seconds}s old)`);
}

Performance

RetailerResponse TimeMethod
Target3-4sDOM extraction (no AI)
eBay6-7sAI extraction (Llama 3.1 8B)
Cached<50msRedis (15min TTL)

POST /api/extract

Extract structured data from any webpage using AI.

POST https://api.injectapi.com/api/extract

Headers

HeaderRequiredDescription
X-API-KeyYesYour API key
Content-TypeYesapplication/json

Request Body

ParameterTypeRequiredDefaultDescription
urlstringYes-URL to scrape
extractbooleanNotrueEnable AI extraction (true = 5-10s, false = 1-2s)
modestringNogeneralExtraction mode (see below)
waitFornumberNo0Wait time in ms for dynamic content
customSchemastringNo-Custom extraction instructions

Extraction Modes

  • general - Extract any structured data
  • product - E-commerce product pages
  • article - Blog posts and news articles
  • profile - Social media profiles
  • contact - Contact information pages
  • search - Search results pages

Response Schemas by Mode

Each extraction mode returns different fields optimized for that content type.

Product Mode

Extracts structured product data from e-commerce pages:

{
  "success": true,
  "data": {
    "title": "Apple AirPods Pro (2nd Generation)",
    "price": 24999,                    // Price in cents
    "currency": "USD",
    "original_price": 29999,           // Optional: original price if on sale
    "discount_percentage": 17,         // Optional: discount percentage
    "availability": "In Stock",
    "sku": "MTJV3AM/A",
    "brand": "Apple",
    "rating": 4.7,
    "review_count": 50234,
    "images": [
      "https://m.media-amazon.com/images/I/61f1YfTkTDL.jpg"
    ],
    "description": "Active Noise Cancellation...",
    "features": [
      "Active Noise Cancellation",
      "Transparency mode",
      "Adaptive Audio"
    ],
    "variants": [                      // Optional: product variants
      {
        "name": "Color",
        "options": ["White"]
      }
    ],
    "shipping": {                      // Optional: shipping info
      "free": true,
      "estimated_delivery": "2-3 days"
    }
  },
  "metadata": {
    "url": "https://amazon.com/dp/B0CHWRXH8B",
    "mode": "product",
    "extracted": true,
    "timestamp": "2025-01-21T10:30:00Z",
    "processingTime": 5234
  }
}

Article Mode

Extracts content from blog posts and news articles:

{
  "success": true,
  "data": {
    "title": "The Future of Web Scraping in 2025",
    "author": "Jane Smith",
    "published_date": "2025-01-15T09:00:00Z",
    "modified_date": "2025-01-16T14:30:00Z",  // Optional
    "content": "Full article text content...",
    "excerpt": "A brief summary or excerpt...",   // Optional
    "image": "https://example.com/featured-image.jpg",
    "tags": ["web scraping", "AI", "technology"],
    "category": "Technology",                   // Optional
    "reading_time_minutes": 8,                 // Optional
    "word_count": 1850,                        // Optional
    "language": "en"                           // Optional
  },
  "metadata": {
    "url": "https://techcrunch.com/article-slug",
    "mode": "article",
    "extracted": true,
    "timestamp": "2025-01-21T10:30:00Z",
    "processingTime": 4567
  }
}

Profile Mode

Extracts information from social media profiles and bio pages:

{
  "success": true,
  "data": {
    "name": "John Doe",
    "username": "johndoe",
    "bio": "Software engineer and tech enthusiast...",
    "avatar": "https://example.com/avatar.jpg",
    "location": "San Francisco, CA",
    "website": "https://johndoe.com",
    "followers": 12500,                        // Optional
    "following": 340,                          // Optional
    "verified": true,                          // Optional
    "social_links": {                          // Optional
      "twitter": "https://twitter.com/johndoe",
      "linkedin": "https://linkedin.com/in/johndoe",
      "github": "https://github.com/johndoe"
    }
  },
  "metadata": {
    "url": "https://twitter.com/johndoe",
    "mode": "profile",
    "extracted": true,
    "timestamp": "2025-01-21T10:30:00Z",
    "processingTime": 3890
  }
}

Contact Mode

Extracts contact information from business pages:

{
  "success": true,
  "data": {
    "name": "Acme Corporation",
    "emails": [
      "contact@acme.com",
      "support@acme.com"
    ],
    "phones": [
      "+1 (555) 123-4567",
      "+1 (555) 987-6543"
    ],
    "address": {
      "street": "123 Main Street",
      "city": "San Francisco",
      "state": "CA",
      "zip": "94102",
      "country": "USA"
    },
    "social_media": {                          // Optional
      "facebook": "https://facebook.com/acme",
      "twitter": "https://twitter.com/acme",
      "linkedin": "https://linkedin.com/company/acme"
    },
    "hours": {                                 // Optional
      "monday": "9:00 AM - 5:00 PM",
      "tuesday": "9:00 AM - 5:00 PM",
      "friday": "9:00 AM - 3:00 PM"
    }
  },
  "metadata": {
    "url": "https://acme.com/contact",
    "mode": "contact",
    "extracted": true,
    "timestamp": "2025-01-21T10:30:00Z",
    "processingTime": 4123
  }
}

Search Mode

Extracts results from search result pages:

{
  "success": true,
  "data": {
    "query": "web scraping tools",
    "results_count": 15,
    "results": [
      {
        "title": "Top 10 Web Scraping Tools in 2025",
        "url": "https://example.com/article",
        "snippet": "Discover the best web scraping tools...",
        "position": 1
      },
      {
        "title": "Web Scraping with Python Tutorial",
        "url": "https://example.com/tutorial",
        "snippet": "Learn how to scrape websites using Python...",
        "position": 2
      }
    ],
    "related_searches": [                      // Optional
      "python web scraping",
      "beautiful soup tutorial"
    ],
    "pagination": {                            // Optional
      "current_page": 1,
      "total_pages": 10
    }
  },
  "metadata": {
    "url": "https://google.com/search?q=web+scraping+tools",
    "mode": "search",
    "extracted": true,
    "timestamp": "2025-01-21T10:30:00Z",
    "processingTime": 5678
  }
}

General Mode

Flexible extraction for any type of content. Returns dynamic fields based on page structure:

{
  "success": true,
  "data": {
    // Dynamic fields based on page content
    // Use customSchema for specific extraction requirements
    "extracted_fields": {
      "field1": "value1",
      "field2": "value2"
    }
  },
  "metadata": {
    "url": "https://example.com",
    "mode": "general",
    "extracted": true,
    "timestamp": "2025-01-21T10:30:00Z",
    "processingTime": 4456
  }
}

Response Format

Success Response

{
  "success": true,
  "data": {
    // Extracted data structure varies by mode
  },
  "metadata": {
    "url": "string",
    "mode": "string",
    "extracted": boolean,
    "timestamp": "ISO 8601",
    "processingTime": number
  }
}

Error Response

{
  "success": false,
  "error": "Error message",
  "metadata": {
    "timestamp": "ISO 8601"
  }
}

Examples

Product Extraction

const response = await fetch('https://api.injectapi.com/api/extract', {
  method: 'POST',
  headers: {
    'X-API-Key': 'ik_...',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    url: 'https://amazon.com/dp/B08N5WRWNW',
    mode: 'product',
    extract: true
  })
});

const data = await response.json();
// {
//   "success": true,
//   "data": {
//     "title": "Apple AirPods Pro",
//     "price": 24900,
//     "currency": "USD",
//     "rating": 4.7,
//     "reviews_count": 50234,
//     ...
//   }
// }

Article Extraction

const response = await fetch('https://api.injectapi.com/api/extract', {
  method: 'POST',
  headers: {
    'X-API-Key': 'ik_...',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    url: 'https://techcrunch.com/article-slug',
    mode: 'article',
    extract: true
  })
});

const data = await response.json();
// {
//   "data": {
//     "title": "Article Title",
//     "author": "Jane Doe",
//     "published_date": "2025-01-15",
//     "content": "Full article text...",
//     "image": "https://...",
//     ...
//   }
// }

Raw HTML (No Extraction)

const response = await fetch('https://api.injectapi.com/api/extract', {
  method: 'POST',
  headers: {
    'X-API-Key': 'ik_...',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    url: 'https://example.com',
    extract: false // Faster, returns raw HTML
  })
});

const data = await response.json();
console.log(data.data.html); // Raw HTML string

GET /api/compare-prices/retailers

Get a list of all supported retailers for price comparison.

GET https://api.injectapi.com/api/compare-prices/retailers

Response

{
  "success": true,
  "retailers": [
    { "id": "target", "name": "Target" },
    { "id": "ebay", "name": "eBay" }
  ]
}

GET /api/scrape

Scrape HTML from a URL without AI extraction (faster, cheaper). Supports retailer-specific presets.

GET https://api.injectapi.com/api/scrape?url={url}&preset={preset}

Query Parameters

ParameterTypeRequiredDescription
urlstringYesURL to scrape
presetstringNoScraping preset: target, ebay, walmart, bestbuy, default

Response

{
  "success": true,
  "url": "https://www.target.com/...",
  "html": "<html>...</html>",
  "metadata": {
    "preset": "target",
    "response_time_ms": 1234
  }
}

GET /api/presets

Get all available extraction presets and their descriptions.

GET https://api.injectapi.com/api/presets

Response

{
  "success": true,
  "presets": [
    {
      "id": "product",
      "name": "Product",
      "description": "Extract product information (title, price, rating, etc.)"
    },
    {
      "id": "article",
      "name": "Article",
      "description": "Extract article content (title, author, publish date, content)"
    },
    {
      "id": "profile",
      "name": "Profile",
      "description": "Extract social media profile information"
    },
    {
      "id": "contact",
      "name": "Contact",
      "description": "Extract contact information (email, phone, address)"
    },
    {
      "id": "search",
      "name": "Search",
      "description": "Extract search results"
    }
  ]
}

GET /api/health

Public - No Auth Required

Health check endpoint for monitoring system status and uptime.

GET https://api.injectapi.com/api/health

Response

{
  "status": "ok",
  "timestamp": "2025-01-19T12:34:56.789Z",
  "uptime": 12345,
  "memory": {
    "used": 123456789,
    "total": 1073741824
  }
}

Error Handling

Understanding errors and how to handle them effectively.

HTTP Status Codes

CodeMeaningDescription
200SuccessRequest successful, data extracted
400Bad RequestInvalid parameters or malformed URL
401UnauthorizedInvalid or missing API key
402Payment RequiredInsufficient credits
429Rate Limit ExceededToo many requests
500Internal Server ErrorUnexpected server error
502Bad GatewayTarget website failed to respond
503Service UnavailableOpenAI quota exceeded
504Gateway TimeoutRequest timed out (30s limit)

Error Response Format

All error responses follow this format:

{
  "success": false,
  "error": {
    "code": "ERROR_CODE",
    "message": "Human-readable error message",
    "field": "parameter_name", // Optional, for validation errors
    "retry_after_seconds": 45  // Optional, for rate limits
  },
  "metadata": {
    "timestamp": "2025-01-15T10:30:00Z"
  }
}

Common Errors

401 Unauthorized

Invalid or missing API key:

{
  "success": false,
  "error": {
    "code": "UNAUTHORIZED",
    "message": "Invalid or missing API key"
  }
}

Solution: Check that you're passing X-API-Key header with a valid key.

429 Rate Limit Exceeded

You've exceeded your plan's rate limit:

{
  "success": false,
  "error": {
    "code": "RATE_LIMIT_EXCEEDED",
    "message": "Rate limit exceeded. Please wait before making more requests",
    "retry_after_seconds": 45,
    "limit": 60,
    "window": "1 minute"
  }
}

Solution: Wait for retry_after_seconds before retrying, or upgrade your plan.

402 Payment Required

Monthly credit limit exhausted:

{
  "success": false,
  "error": {
    "code": "INSUFFICIENT_CREDITS",
    "message": "You have used all available credits for this billing period",
    "credits_remaining": 0,
    "next_reset": "2025-02-01T00:00:00Z"
  }
}

Solution: Upgrade plan or wait for monthly reset.

502 Bad Gateway

Target website failed to respond:

{
  "success": false,
  "error": {
    "code": "UPSTREAM_ERROR",
    "message": "Failed to fetch the requested URL",
    "url": "https://example.com/product",
    "status_code": 503
  }
}

Solution: The target website may be down or blocking requests. Try again later.

Retry Logic Best Practices

1. Check Status Code

const response = await fetch('https://api.injectapi.com/api/extract', options);

if (!response.ok) {
  const error = await response.json();
  console.error('API Error:', error);
  throw new Error(error.error.message);
}

const data = await response.json();

2. Implement Retry with Backoff

async function fetchWithRetry(url, options, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    const response = await fetch(url, options);

    // Success
    if (response.ok) {
      return response.json();
    }

    // Rate limit - wait and retry
    if (response.status === 429) {
      const error = await response.json();
      const waitTime = error.error.retry_after_seconds || 60;
      await new Promise(resolve => setTimeout(resolve, waitTime * 1000));
      continue;
    }

    // Server error - exponential backoff
    if (response.status >= 500) {
      const waitTime = Math.pow(2, i) * 1000; // 1s, 2s, 4s
      await new Promise(resolve => setTimeout(resolve, waitTime));
      continue;
    }

    // Client error - don't retry
    throw new Error(`Request failed: ${response.status}`);
  }

  throw new Error('Max retries exceeded');
}

3. Handle Specific Errors

try {
  const response = await fetch('https://api.injectapi.com/api/extract', options);
  const data = await response.json();

  if (!response.ok) {
    switch (response.status) {
      case 401:
        console.error('Invalid API key');
        break;
      case 402:
        console.error('Insufficient credits - upgrade plan');
        break;
      case 429:
        console.error('Rate limit exceeded - slow down');
        break;
      case 502:
      case 504:
        console.error('Target website unreachable - try later');
        break;
      default:
        console.error('Unexpected error:', data.error.message);
    }
  }
} catch (error) {
  console.error('Network error:', error);
}

Rate Limits

Understanding request limits and quotas.

Limits by Plan

PlanRequests/MinuteMonthly CreditsOverage
Free10 RPM1,000Hard limit
Starter60 RPM25,000$3.00/1k
Professional300 RPM100,000$2.50/1k
EnterpriseCustomUnlimitedCustom

Rate Limit Headers

Every API response includes headers with your current rate limit status:

HeaderDescription
X-RateLimit-LimitYour plan's rate limit (requests per minute)
X-RateLimit-RemainingRemaining requests in current window
X-RateLimit-ResetUnix timestamp when limit resets
X-Credits-RemainingMonthly credits remaining
X-Credits-ResetISO 8601 timestamp when credits reset
HTTP/2 200 OK
Content-Type: application/json
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 45
X-RateLimit-Reset: 1705334400
X-Credits-Remaining: 23450
X-Credits-Reset: 2025-02-01T00:00:00Z

Handling Rate Limits

Monitor Headers

const response = await fetch('https://api.injectapi.com/api/extract', options);

// Check rate limit headers
const remaining = response.headers.get('X-RateLimit-Remaining');
const reset = response.headers.get('X-RateLimit-Reset');

console.log(`Requests remaining: ${remaining}`);

if (remaining < 5) {
  console.warn('Approaching rate limit!');
}

Request Queuing

class RateLimitedQueue {
  constructor(requestsPerMinute) {
    this.limit = requestsPerMinute;
    this.requestTimes = [];
  }

  async enqueue(fn) {
    // Wait if at limit
    while (this.requestTimes.length >= this.limit) {
      const oldestRequest = this.requestTimes[0];
      const timeSinceOldest = Date.now() - oldestRequest;

      if (timeSinceOldest < 60000) {
        await new Promise(resolve =>
          setTimeout(resolve, 60000 - timeSinceOldest)
        );
      }

      // Remove requests older than 1 minute
      this.requestTimes = this.requestTimes.filter(
        time => Date.now() - time < 60000
      );
    }

    this.requestTimes.push(Date.now());
    return fn();
  }
}

// Usage
const queue = new RateLimitedQueue(60); // 60 RPM for Starter plan
const results = await Promise.all(
  urls.map(url => queue.enqueue(() => fetchData(url)))
);

Historical Price Tracking

InjectAPI automatically stores every scraped product in PostgreSQL/TimescaleDB for historical price tracking, trend analysis, and competitive intelligence.

Overview

Every API call performs a dual-write to both Redis (ephemeral cache) and PostgreSQL (persistent storage). This enables you to build price tracking SaaS, market research dashboards, and arbitrage tools without additional storage infrastructure.

  • Auto-Storage: Every scraped product is automatically stored with price, availability, rating, etc.
  • 70-80% Data Completeness: High capture rate for price, availability, title, image, rating, reviews
  • Daily Aggregates: Pre-computed min/max/avg prices per day
  • Price Change Detection: Identify 24h price drops/increases for alerts

GET /api/price-history

Get full price history for a specific product URL.

GET https://api.injectapi.com/api/price-history?url=PRODUCT_URL&limit=100

Query Parameters

ParameterTypeRequiredDescription
urlstringYesFull product URL
limitnumberNoMax observations to return (default: 100)

Example Request

curl "https://api.injectapi.com/api/price-history?url=https://www.target.com/p/nintendo-switch/A-12345&limit=100" \
  -H "X-API-Key: YOUR_API_KEY"

Response

{
  "success": true,
  "product": {
    "id": 12345,
    "url": "https://www.target.com/p/nintendo-switch/A-12345",
    "title": "Nintendo Switch OLED Model",
    "retailer": "target",
    "first_seen_at": "2025-11-01T10:30:00Z",
    "last_seen_at": "2025-11-19T15:45:00Z",
    "times_scraped": 847
  },
  "price_history": [
    {
      "id": 98765,
      "price": 349.99,
      "currency": "USD",
      "availability": "In Stock",
      "rating": 4.8,
      "review_count": 5234,
      "scraped_at": "2025-11-19T10:30:00Z"
    }
  ],
  "metadata": {
    "total_observations": 847,
    "returned_observations": 100,
    "earliest_observation": "2025-11-01T10:30:00Z",
    "latest_observation": "2025-11-19T15:45:00Z"
  }
}

GET /api/price-history/daily

Get daily price aggregates (min/max/avg) for a product over the past N days.

GET https://api.injectapi.com/api/price-history/daily?url=PRODUCT_URL&days=30

Query Parameters

ParameterTypeRequiredDescription
urlstringYesFull product URL
daysnumberNoNumber of days to retrieve (default: 30)

Response

{
  "success": true,
  "daily_stats": [
    {
      "day": "2025-11-19",
      "min_price": 299.99,
      "max_price": 349.99,
      "avg_price": 324.99,
      "closing_price": 299.99,
      "sample_count": 8,
      "last_availability": "in_stock"
    }
  ]
}

GET /api/price-history/latest

Get the most recent price observation for a product.

GET https://api.injectapi.com/api/price-history/latest?url=PRODUCT_URL

Response

{
  "success": true,
  "latest_price": {
    "price": 299.99,
    "currency": "USD",
    "availability": "In Stock",
    "rating": 4.8,
    "review_count": 5234,
    "scraped_at": "2025-11-19T15:45:00Z"
  }
}

GET /api/price-history/changes

Get recent price changes (24h drops/increases) across all tracked products.

GET https://api.injectapi.com/api/price-history/changes?retailer=target&limit=50

Query Parameters

ParameterTypeRequiredDescription
retailerstringNoFilter by retailer
limitnumberNoMax products to return (default: 50)

Use Case: Price Drop Alerts

// Daily cron job: Check for price changes
const response = await fetch('/api/price-history/changes?retailer=amazon&limit=100');
const { data } = await response.json();

// Filter for significant drops (10%+)
const bigDrops = data.filter(product => product.price_change_pct < -10);

// Send alerts to users
bigDrops.forEach(product => {
  sendPriceAlert(user, {
    title: product.title,
    oldPrice: product.price_24h_ago,
    newPrice: product.current_price,
    savings: product.price_change_amount
  });
});

Search for products in the historical database.

GET https://api.injectapi.com/api/price-history/search?q=nintendo+switch&retailer=target

Query Parameters

ParameterTypeRequiredDescription
qstringYesSearch query (title search)
retailerstringNoFilter by retailer

GET /api/price-history/stats

Get database statistics (total products, price observations, retailer breakdown).

GET https://api.injectapi.com/api/price-history/stats

Response

{
  "success": true,
  "stats": {
    "totalProducts": 12453,
    "totalPriceObservations": 45621,
    "retailerBreakdown": {
      "target": 5234,
      "ebay": 4123,
      "amazon": 3096
    },
    "oldestData": "2025-11-01T10:30:00Z",
    "newestData": "2025-11-19T15:45:00Z"
  }
}

Use Cases

1. Price Tracking SaaS

Build a JungleScout or Keepa competitor with historical price data:

  • Show 30-day price charts with min/max/avg
  • Send price drop alerts when products hit target prices
  • Track competitor pricing strategies

2. Market Research Dashboard

Analyze pricing trends across retailers:

  • Compare average prices by retailer
  • Identify seasonal pricing patterns
  • Track availability changes

3. Arbitrage Tool

Find profitable arbitrage opportunities:

  • Detect products with recent 20%+ price drops
  • Cross-reference with other marketplaces
  • Alert users to buying opportunities

Need Help?