Python SDK Specification

    Python SDK Specification

    Examples on this page match PyPI desearch-py==1.2.1. Do not call extract() on this release. Use web_crawl or REST GET /web/extract.

    First AI Search call, with the same fields as the Search API guide: Extract API · Auth and API keys.

    Install

    Pin the version used in these examples:

    bash
    pip install desearch-py==1.2.1

    Construct the client

    Pass the raw API key from the environment. Do not hardcode production keys.

    python
    import asyncio import os from desearch_py import Desearch async def main(): desearch = Desearch(api_key=os.environ["DESEARCH_API_KEY"]) # Prefer an async context manager if the published client supports it. result = await desearch.ai_search( prompt="latest AI search API notes", tools=["web"], count=10, ) print(result) asyncio.run(main())

    Do not pass streaming into ai_search. That argument is not in the current signature. For REST streaming, use the Streaming AI Search guide.

    Extract / crawl

    On 1.2.1, the extract family is web_crawl only:

    python
    content = await desearch.web_crawl( url="https://en.wikipedia.org/wiki/Artificial_intelligence", format="text", )

    Do not show or call desearch.extract(...) until PyPI ships it with evidence. REST alternative: GET /web/extract on the Extract API page.

    Billing metadata

    Read request cost from response headers. JSON object bodies may also include cost_usd, usage_count, service, and currency.

    HeaderMeaning
    X-Desearch-Cost-UsdRequest cost in USD
    X-Desearch-Usage-CountBillable usage units
    X-Desearch-ServicePricing service key
    X-Desearch-CurrencyCurrency code

    Arrays, text responses, and streaming responses keep their body shape. Use headers for those. This page does not list dollar rates.

    Errors

    • Missing or invalid key: 401.
    • Validation failure: 4xx with the API message.
    • 429 or 5xx: retry with backoff.

    Do not invent error JSON schemas beyond the API Reference.

    Parity with JavaScript

    CapabilityPython 1.2.1JavaScript 1.5.1
    AI Searchai_search(...)aiSearch({...})
    Extract page contentREST /web/extract or web_crawlextract({...})
    Streaming helpersNot on this package. Use REST (streaming guide)Same
    Billing fields on a JSON objectRead body and headersSame
    Billing on a streamHeaders onlyHeaders only
    People Search (Python 1.4.0, JavaScript 1.6.0)x_profile_search(...), youtube_creator_search(...), linkedin_profile_search(...), tiktok_creator_search(...)xProfileSearch({...}), youtubeCreatorSearch({...}), linkedinProfileSearch({...}), tiktokCreatorSearch({...})
    Wait for a People Search taskwait_for_people_task(task_id)waitForPeopleTask(task)
    Read every People Search resultiter_people_results(task_id)iterPeopleResultPages({ task_id })
    People Search metadata labels (Python 1.5.0, JavaScript 1.7.0)metadata={...} on the search methods and list_people_tasksmetadata: {...} in the search request and in listPeopleTasks

    Other methods already on this package

    Web search:

    python
    result = desearch.web_search( query="latest AI search news", num=10, start=0, ) print(result)

    X search:

    python
    result = desearch.x_search( query="Bittensor", sort="Top", count=20, ) print(result)

    People Search (1.4.0 and later)

    People Search methods ship in desearch-py 1.4.0, and metadata labels in 1.5.0. The examples above match 1.2.1.

    1.5.0 is not on PyPI yet. Until it is, install it from GitHub:

    bash
    pip install "desearch-py @ git+https://github.com/Desearch-ai/desearch.py@fa4f4270c9069490ab871bc13fba5d5cfda3b310"

    Once 1.5.0 is on PyPI, pip install "desearch-py>=1.5.0" works the same way. You can also call the People Search REST endpoints directly with any HTTP client: see People Search.

    A search runs in the background. Start it, wait for it, then read or export the people it found:

    python
    import asyncio import os import uuid from desearch_py import Desearch async def main(): async with Desearch(api_key=os.environ["DESEARCH_API_KEY"]) as desearch: # The only billed call. It returns a queued task at once. task = await desearch.x_profile_search( query="Indie hackers building in public who share their MRR", count=10, idempotency_key=str(uuid.uuid4()), ) # Polls every 5 seconds, up to 10 minutes. Free. task = await desearch.wait_for_people_task(task.task_id) async for person in desearch.iter_people_results(task.task_id): print(person.handle, person.score.value if person.score else None) csv_text = await desearch.export_people_results(task.task_id, format="csv") print(len(csv_text)) asyncio.run(main())
    MethodRoute
    x_profile_search, youtube_creator_search, linkedin_profile_search, tiktok_creator_searchPOST /desearch/people/search/{x,youtube,linkedin,tiktok} (billed)
    list_people_tasksGET /desearch/people/tasks
    get_people_taskGET /desearch/people/tasks/{task_id}
    get_people_resultsGET /desearch/people/tasks/{task_id}/results
    pause_people_task, resume_people_task, cancel_people_taskPOST /desearch/people/tasks/{task_id}/pause, /resume, /cancel
    export_people_resultsGET /desearch/people/tasks/{task_id}/export
    list_people_profilesGET /desearch/people/profiles
    export_people_profilesGET /desearch/people/profiles/export
    wait_for_people_taskPolls get_people_task until the task ends
    iter_people_resultsFollows next_cursor on get_people_results

    Search methods take query, count, min_score, exclude, and filters (XProfileSearchFilters, YouTubeCreatorSearchFilters, LinkedInProfileSearchFilters, TikTokCreatorSearchFilters, or a dict), plus metadata from 1.5.0 (below). idempotency_key is sent as the Idempotency-Key header. A 409 from pause, resume, or cancel raises PeopleTaskConflictError. A timeout in wait_for_people_task raises PeopleTaskTimeoutError; the search keeps running. Flow, filters, and billing: People Search.

    Label searches with metadata (1.5.0 and later)

    desearch-py 1.5.0 adds metadata to the four search methods and to list_people_tasks. Use it to tag each search with your own labels, such as the campaign, workspace, or customer it belongs to, and to list those searches later. The search itself never uses the labels.

    The GitHub install above is 1.5.0, so it includes metadata.

    python
    import asyncio import os import uuid from desearch_py import Desearch async def main(): async with Desearch(api_key=os.environ["DESEARCH_API_KEY"]) as desearch: task = await desearch.x_profile_search( query="Indie hackers building in public who share their MRR", count=10, metadata={"campaign": "q4-outbound", "workspace_id": "ws_42"}, idempotency_key=str(uuid.uuid4()), ) print(task.metadata) # {'campaign': 'q4-outbound', 'workspace_id': 'ws_42'} # Later: every search of that campaign, newest first. page = await desearch.list_people_tasks(metadata={"campaign": "q4-outbound"}) for t in page.tasks: print(t.task_id, t.status, t.metadata.get("workspace_id")) asyncio.run(main())
    • task.metadata is {} for a search without labels.
    • A search takes up to 10 keys. Keys are 1 to 40 characters of a-z, 0-9, _, ., or -, and values are strings of up to 200 characters. The SDK does not check them; the API answers 422, raised as DesearchAPIError.
    • list_people_tasks sends each pair as a metadata.<key>=<value> query parameter, and a task must match all of them exactly. Pass up to 3 pairs (the API answers 400 otherwise), and pass the same metadata with each cursor when you page.
    • This metadata is the task's labels. It is not the cost wrapper that include_metadata=True returns.

    Next