AlterLabAlterLab
PricingComparePlaygroundBlogDocsChangelog
    AlterLabAlterLab
    PricingComparePlaygroundBlogDocsChangelog
    IntroductionQuickstartInstallationYour First Request
    REST APICrawl APIMap APISearch APIExtract APIJob PollingAPI KeysSessions APINew
    OverviewPythonNode.js
    JavaScript RenderingOutput FormatsPDF & OCRCachingWebhooksJSON Schema FilteringWebSocket Real-TimeBring Your Own ProxyProAuthenticated ScrapingNewHTTP Methods & BodiesNewStructured ExtractionAIWeb SearchSite MappingWeb CrawlingBatch ScrapingSchedulerChange DetectionCloud Storage ExportSpend LimitsOrganizations & TeamsAlerts & NotificationsOAuth2 Machine-to-MachineSupport & Tickets
    Structured ExtractionAIE-commerce ScrapingNews MonitoringPrice MonitoringMulti-Page CrawlingMonitoring DashboardAI Agent / MCPMCPAI Research AgentAISite CrawlingData Pipeline to Cloud
    PricingRate LimitsError CodesChangelogVersioning
    From FirecrawlFrom ApifyFrom ScrapingBee / ScraperAPIFrom Crawl4AIFrom SpiderFirecrawl v0 API ReferenceLegacy
    PlaygroundPricingStatus
    API Reference
    New

    Search API

    Find relevant web pages by keyword before scraping. Search returns URLs, titles, and snippets — optionally scraping each result inline.

    Discovery-First Workflow

    Search is designed for cases where you know what you want but not where it lives. Find pages first, then scrape and extract in a single pipeline.

    Overview

    1

    Search

    POST a query to /api/v1/search. Get back URLs, titles, and snippets from Google.

    2

    Scrape (Optional)

    Set scrape_results: true to scrape every result page and get full content back.

    3

    Extract (Optional)

    Pass an extraction_schema to pull structured data from every result in one call.

    POST /v1/search

    POST
    /api/v1/search

    Execute a web search. Returns results synchronously (200) or, when scrape_results is true and there are more than 5 results, returns 202 with a search_id for polling.

    Bash
    curl -X POST https://api.alterlab.io/api/v1/search \
      -H "X-API-Key: your_api_key" \
      -H "Content-Type: application/json" \
      -d '{
        "query": "best headless browsers 2026",
        "num_results": 10
      }'

    Request Body

    ParameterTypeRequiredDescription
    querystringYesSearch terms (1-500 characters)
    domainstringNoRestrict results to a specific domain (applied as site: prefix)
    num_resultsintegerNoNumber of results to return (1-50, default: 10)
    countrystringNoISO 3166-1 alpha-2 country code for geo-targeted results (e.g., US, GB, DE)
    languagestringNoLanguage code for results (e.g., en, fr, de)
    time_rangestringNoFilter by recency: hour, day, week, month, year
    scrape_resultsbooleanNoIf true, scrape each result page and include full content (default: false)
    formatsstring[]NoOutput formats when scrape_results=true: text, json, json_v2, html, markdown
    extraction_schemaobjectNoJSON schema for structured extraction (when scrape_results=true)

    Search-Only Response (200)

    When scrape_results is false (default), results are returned synchronously:

    JSON
    {
      "search_id": "a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
      "query": "best headless browsers 2026",
      "results_count": 10,
      "credits_used": 2000,
      "results": [
        {
          "url": "https://example.com/headless-browsers-guide",
          "title": "Top 10 Headless Browsers in 2026",
          "snippet": "A comprehensive comparison of the best headless browsers for web scraping and automation...",
          "position": 1,
          "content": null
        },
        {
          "url": "https://blog.example.com/playwright-vs-puppeteer",
          "title": "Playwright vs Puppeteer in 2026",
          "snippet": "Which headless browser framework should you choose? We compare performance, features...",
          "position": 2,
          "content": null
        }
      ]
    }

    Search + Scrape Response (202)

    When scrape_results=true and there are more than 5 results, the response is 202 with a polling URL:

    JSON
    {
      "search_id": "a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
      "query": "best headless browsers 2026",
      "status": "scraping",
      "results_count": 10,
      "credits_used": 52000,
      "results": [ ... ],
      "message": "Search complete. 10 results are being scraped. Poll GET /api/v1/search/a1b2c3d4-... for progress."
    }

    Inline Scraping

    When scrape_results=true and there are 5 or fewer results, the API waits briefly and attempts to return content inline. For larger result sets, use the polling endpoint.

    GET /api/v1/search/{search_id}

    GET
    /api/v1/search/{search_id}

    Poll for search + scrape progress. Returns the search results with scraped content populated as each job completes.

    Bash
    curl https://api.alterlab.io/api/v1/search/a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d \
      -H "X-API-Key: your_api_key"

    Status Response

    JSON
    {
      "search_id": "a1b2c3d4-...",
      "query": "best headless browsers 2026",
      "status": "completed",
      "results_count": 10,
      "completed": 10,
      "credits_used": 52000,
      "results": [
        {
          "url": "https://example.com/headless-browsers-guide",
          "title": "Top 10 Headless Browsers in 2026",
          "snippet": "A comprehensive comparison...",
          "position": 1,
          "content": {
            "text": "Full article text here...",
            "markdown": "# Top 10 Headless Browsers..."
          }
        }
      ]
    }
    StatusMeaning
    scrapingSome result pages are still being scraped
    completedAll result pages have been scraped

    24-Hour TTL

    Search metadata expires after 24 hours. Poll for results before then.

    Batch Search

    Submit up to 50 search queries for async parallel processing. Each query runs independently and results are aggregated. Requires Growth+ balance tier ($50+ balance). Poll for results or receive them via webhook.

    Request Body

    POST
    /api/v1/search/batch

    Submit a batch of search queries for asynchronous processing.

    Parameters

    NameTypeRequiredDescription
    queriesstring[]
    Required
    Search queries to execute (1-50 queries, each max 500 characters)
    num_resultsintegerOptionalNumber of results per query (1-30)Default: 10
    countrystringOptionalISO 3166-1 alpha-2 country code applied to all queries (e.g., US, GB, DE)
    languagestringOptionalLanguage code applied to all queries (e.g., en, fr, de)
    time_rangestringOptionalFilter results by recency: hour, day, week, month, year
    scrape_resultsbooleanOptionalIf true, scrape each result page for content. Per-page tier costs are debited as scrapes complete.Default: false
    formatsstring[]OptionalOutput formats when scrape_results=true: text, json, json_v2, html, markdown
    webhook_urlstringOptionalWebhook URL to POST when all queries complete. Receives a batch.search.completed event.

    Request Example

    Bash
    curl -X POST https://api.alterlab.io/api/v1/search/batch \
      -H "X-API-Key: sk_live_..." \
      -H "Content-Type: application/json" \
      -d '{
        "queries": [
          "web scraping best practices",
          "structured data extraction",
          "API rate limiting strategies"
        ],
        "num_results": 10,
        "country": "US",
        "webhook_url": "https://your-app.com/webhooks/search"
      }'

    Response Example

    JSON
    {
      "batch_id": "b1c2d3e4-f5a6-7890-bcde-f12345678901",
      "total_queries": 3,
      "status": "processing",
      "estimated_credits": 3,
      "job_ids": [
        "j1-search-001",
        "j1-search-002",
        "j1-search-003"
      ],
      "message": "Batch search submitted. Poll GET /api/v1/search/batch/{batch_id} for results."
    }

    Response

    The POST response returns immediately with a 202 Accepted status. Use the batch_id to poll for results.

    Python SDK Example

    Python
    from alterlab import AlterLabSync
    
    client = AlterLabSync(api_key="YOUR_API_KEY")
    
    # Submit batch search
    batch = client.batch_search(
        queries=[
            "web scraping best practices",
            "structured data extraction",
            "API rate limiting strategies"
        ],
        num_results=10,
        country="US"
    )
    
    print(f"Batch ID: {batch['batch_id']}")
    print(f"Estimated credits: {batch['estimated_credits']}")
    
    # Poll for results
    import time
    while True:
        status = client.get_batch_search_status(batch["batch_id"])
        if status["status"] != "processing":
            break
        print(f"Progress: {status['completed']}/{status['total']}")
        time.sleep(2)
    
    # Process results
    for item in status["items"]:
        if item["status"] == "succeeded":
            print(f"Query: {item['query']}")
            for result in item["results"]:
                print(f"  {result['position']}. {result['title']}")

    Poll Batch Search Status

    Poll the status of a batch search to get per-query results as they complete.

    GET
    /api/v1/search/batch/{batch_id}

    Get the current status and results of a batch search.

    Parameters

    NameTypeRequiredDescription
    batch_idstring
    Required
    Batch search ID returned from POST /search/batch

    Request Example

    Bash
    curl -X GET https://api.alterlab.io/api/v1/search/batch/b1c2d3e4 \
      -H "X-API-Key: sk_live_..."

    Response Example

    JSON
    {
      "batch_id": "b1c2d3e4-f5a6-7890-bcde-f12345678901",
      "status": "completed",
      "total": 3,
      "completed": 3,
      "failed": 0,
      "pending": 0,
      "items": [
        {
          "job_id": "j1-search-001",
          "query": "web scraping best practices",
          "status": "succeeded",
          "results_requested": 10,
          "results_count": 10,
          "results": [
            {
              "url": "https://example.com/scraping-guide",
              "title": "Web Scraping Best Practices Guide",
              "snippet": "Learn the best practices for building reliable web scrapers...",
              "position": 1
            }
          ],
          "credits_used": 1
        }
      ],
      "total_credits_used": 3,
      "created_at": "2025-11-05T10:30:00Z",
      "webhook_status": "delivered",
      "webhook_attempts": 1
    }
    StatusDescription
    processingQueries are still being executed
    completedAll queries finished successfully
    partialAll queries finished but some failed
    failedAll queries failed

    Batch Search Requirements

    • Requires Growth+ balance tier ($50+ balance)
    • Maximum 50 queries per batch
    • Each query is billed at the flat search cost up front
    • When scrape_results=true, per-page tier costs are debited as scrapes complete
    • Batch metadata expires after 24 hours

    Credit Model

    ActionCostNotes
    Search query2 creditsFlat fee per search, regardless of num_results
    Scrape per result1-5 creditsStandard scrape pricing per URL (tier-based)
    ExtractionIncludedNo extra charge when using extraction_schema with scrape_results

    BYOP Discount

    If you have a Bring Your Own Proxy integration, scrape credits are discounted per your plan. The search query cost (2 credits) is not affected.

    Error Codes

    StatusErrorDescription
    402insufficient_creditsNot enough credits for the search or scrape
    404search_not_foundSearch ID not found or expired (polling endpoint)
    502search_provider_errorUpstream search provider returned an error
    503search_unavailableSearch service is not configured
    504search_timeoutSearch provider timed out

    Examples

    Basic Search

    Python
    import requests
    
    response = requests.post(
        "https://api.alterlab.io/api/v1/search",
        headers={"X-API-Key": "YOUR_API_KEY"},
        json={
            "query": "web scraping best practices",
            "num_results": 5
        }
    )
    
    data = response.json()
    for result in data["results"]:
        print(f"{result['position']}. {result['title']}")
        print(f"   {result['url']}")

    Domain-Scoped Search

    Python
    # Search within a specific domain
    response = requests.post(
        "https://api.alterlab.io/api/v1/search",
        headers={"X-API-Key": "YOUR_API_KEY"},
        json={
            "query": "authentication API",
            "domain": "docs.github.com",
            "num_results": 10
        }
    )
    
    # All results will be from docs.github.com
    data = response.json()
    print(f"Found {data['results_count']} pages on docs.github.com")

    Search + Scrape with Extraction

    Python
    import time
    
    # Search and scrape with structured extraction
    response = requests.post(
        "https://api.alterlab.io/api/v1/search",
        headers={"X-API-Key": "YOUR_API_KEY"},
        json={
            "query": "iPhone 16 Pro review",
            "num_results": 5,
            "scrape_results": True,
            "formats": ["text", "markdown"],
            "extraction_schema": {
                "type": "object",
                "properties": {
                    "rating": {"type": "number", "description": "Review rating out of 10"},
                    "pros": {"type": "array", "items": {"type": "string"}},
                    "cons": {"type": "array", "items": {"type": "string"}},
                    "verdict": {"type": "string", "description": "One-line summary"}
                }
            }
        }
    )
    
    data = response.json()
    
    # If 202, poll for results
    if response.status_code == 202:
        search_id = data["search_id"]
        while True:
            status = requests.get(
                f"https://api.alterlab.io/api/v1/search/{search_id}",
                headers={"X-API-Key": "YOUR_API_KEY"}
            ).json()
            if status["status"] == "completed":
                data = status
                break
            time.sleep(2)
    
    # Process extracted data
    for result in data["results"]:
        if result.get("content") and result["content"].get("extraction"):
            ext = result["content"]["extraction"]
            print(f"{result['title']}: {ext.get('rating')}/10")
            print(f"  Verdict: {ext.get('verdict')}")
    ← Previous: REST APINext: Search Guide →
    Last updated: March 2026

    On this page