Pplx search sdk
Skill perplexityai/api-platform-developers/skills/pplx-search-sdk
Install and use pplx-srch-sdk, the public Python SDK for the Perplexity Search API (import pplx_srch_sdk): live web search and query-relevant page snippets from Python code. Use to search the web from a script or notebook, fan many searches out concurrently, get query-relevant excerpts from specific URLs, build multi-step research pipelines, or handle Search API errors with typed exceptions.From its SKILL.md
npx -y skills add perplexityai/api-platform-developers --skill pplx-search-sdkAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
SKILL.md
10.1 KB, as published. Nobody here has run it
pplx_srch_sdk - Python Search SDK
What this skill does
Installs and drives pplx-srch-sdk, the public Python SDK for the Search API: pplx_srch_sdk.search.web for live web search, pplx_srch_sdk.search.web_many for concurrent fan-out over many queries, pplx_srch_sdk.content.snippets for query-relevant page excerpts.
The sync facade covers most work; pplx_srch_sdk.AsyncPplxClient exposes the same surface async.
Install and auth
pip install pplx-srch-sdk
Python >= 3.12 on Linux (x86_64, aarch64) and macOS. Package and import share the name: pip install pplx-srch-sdk, then import pplx_srch_sdk.
Get an API key at https://www.perplexity.ai/account/api and export it:
export PERPLEXITY_API_KEY=pplx-...
Import succeeds without a key; a missing key raises AuthenticationError at first use (pitfall 8).
Web search
import pplx_srch_sdk
hits = pplx_srch_sdk.search.web("rust async runtimes", limit=5)
for hit in hits:
print(hit.title, hit.url)
search.web returns a bare list[WebHit] (pitfall 1).
Keep queries short keyword phrases, usually 2-5 meaningful words, one topic per query; no quote marks, site:, or boolean AND/OR - use kwargs like domains=[...] and excluded_domains=[...] instead.
Break multi-entity questions into separate single-entity queries and send them through web_many, not one long combined query.
A list passed as the first argument is reformulations of ONE query, up to 10, merged into a single result list (pitfall 2).
On non-trivial searches, add intent= - one short sentence stating what the search should find or verify; it sharpens which text comes back for each hit without changing which pages are found.
Full kwarg table (domains, country, date bounds, intent, token budgets): references/search.md.
Fan-out (many independent queries)
results = pplx_srch_sdk.search.web_many(
[
"yellowstone national park visitor numbers 2025",
"zion national park entrance fee 2026",
"acadia national park camping reservations",
],
limit_per_query=10,
)
for r in results:
if r.ok:
print(r.spec["query"], "->", len(r.result), "hits")
else:
print(r.spec["query"], "failed:", r.error)
web_many runs one independent search per query with bounded concurrency (default 5) and returns one FanoutResult per query, in input order.
Read r.ok / r.spec / r.result / r.error (pitfall 3); one failed query does not fail the batch.
Queries can be plain strings or per-query kwargs dicts ({"query": ..., "domains": [...]}); extra kwargs on the call apply to every query.
Mechanics plus the pplx_srch_sdk.utils helpers (partition, flatten, dedup, JSONL artifacts): references/fanout.md.
Snippets
snips = pplx_srch_sdk.content.snippets(
query="what's new in python 3.13",
urls=[
"https://docs.python.org/3/whatsnew/3.13.html",
"https://example.com/missing",
],
)
for s in snips:
print(s.url, s.error or (s.text or "")[:200])
One SnippetResult per input URL, in input order.
Check error on every result before trusting text (pitfall 4).
Use text, not content; elided regions inside text are marked with ….
Token budgets and URL limits: references/content.md.
Result shapes
search.web(...)->list[WebHit]; each hit has{url, title, domain, snippet, date?, last_updated?}.snippetis the text field for the hit and is an empty string when no text is available;dateis the publication date andlast_updatedthe last-modified date.- Typed records support attribute reads (
hit.url), mapping access (hit["url"],{**hit},hit.keys()), anddict(hit)/hit.to_dict()for JSON-serializable rows. Missing optional fields read asNone; the mapping view contains only populated fields. search.web_many(...)->list[FanoutResult]with.ok/.spec/.result/.error(pitfall 3; details in references/fanout.md).content.snippets(...)->list[SnippetResult]; each has{url, text?, tokens_count?, error?}.
Errors
Every SDK exception derives from pplx_srch_sdk.PplxSdkError; API failures raise subclasses of pplx_srch_sdk.APIError: AuthenticationError (401), ForbiddenError (403), NotFoundError (404), NotAcceptableError (406), TimeoutError (408, client-side - ~10 s for search, 30 s for snippets, not configurable), RateLimitError (429), BadRequestError (400), ValidationError (422), InternalServerError (5xx), ConnectError (network failure).
TimeoutError here is pplx_srch_sdk.TimeoutError, not the builtin: except TimeoutError: will not catch it - catch APIError or import it explicitly; it is excluded from import *.
Every APIError also carries e.status_code (int) and e.body (str); branch on e.status_code, not the message text.
import time
import pplx_srch_sdk
for attempt in range(3):
try:
hits = pplx_srch_sdk.search.web("query", limit=5)
break
except pplx_srch_sdk.RateLimitError:
if attempt == 2:
raise
time.sleep(2**attempt) # bounded backoff: 1 s, then 2 s
except pplx_srch_sdk.APIError as e:
print(e.status_code, e.body) # e.g. 400 and the server's error detail
break
The SDK never retries or throttles - each call sends exactly one billable HTTP request. On RateLimitError, lower concurrency, wait, then re-dispatch only the failed specs.
Inside web_many, per-query API errors land on FanoutResult.error instead of raising; a missing API key still raises AuthenticationError immediately, before any query is dispatched.
Async
import asyncio
import pplx_srch_sdk
async def main():
async with pplx_srch_sdk.AsyncPplxClient() as client:
hits = await client.search.web("python 3.13 release notes", limit=10)
print(len(hits), hits[0].url)
asyncio.run(main())
Drop to AsyncPplxClient for a custom concurrency model or long-lived concurrent state; there is no async web_many - use pplx_srch_sdk.utils.fanout with client.search.web (see references/async.md).
Reuse one client per script (pitfall 6).
Top pitfalls
search.webreturns a barelist[WebHit]. There is no.resultswrapper or response envelope:hits[0].urlworks directly,hits.resultsis anAttributeError.- A list passed to
web()is reformulations of ONE query, not a batch. Up to 10 variants merge into a single result list, andlimitcaps that whole merged set, not each variant. N distinct questions =web_manywithlimit_per_query. FanoutResultexposesspec/result/error/ok.r.resultis the hit list andr.specthe request kwargs; there is no.requestattribute. Always branch onr.ok- per-query failures ride the batch asr.error, they do not raise.- A successful
content.snippetscall does not mean every URL succeeded. Per-URL failures setresults[i].errorand leavetextunset; checkerroron each result before usingtext. - Date filter kwargs use MM/DD/YYYY strings, not ISO dates (e.g.
published_after_date="7/1/2026"); padding is optional.published_after_date/published_before_datecannot be combined withrecency_filter; the server rejects these combinations. - Do not construct one
AsyncPplxClientper coroutine insideasyncio.gather. That leaks connections - reuse one client, or stay onweb_many/pplx_srch_sdk.utils.fanout. - A conflicting
limitalongsidelimit_per_queryonweb_manyraisesTypeError. Equal values are accepted but redundant; preferlimit_per_queryalone, which forwards aslimit=to each single search. - A missing key fails at call time, not import time.
import pplx_srch_sdksucceeds withoutPERPLEXITY_API_KEY; the first sync-facade call raisesAuthenticationError: [401] No API key configured. Set the PERPLEXITY_API_KEY environment variable, andAsyncPplxClient()raises the same error eagerly at construction - wrap client creation, not just the awaited calls (as examples/async_client.py does).
Reference index
- references/search.md - read when composing queries or filters: full
search.webkwarg table, query-style rules, date filters,WebHitshape. - references/fanout.md - read when running many queries:
web_manymechanics,FanoutResulthandling,pplx_srch_sdk.utilshelpers for artifacts and dedup. - references/content.md - read when excerpting known URLs:
content.snippetstoken budgets, per-URL error handling. - references/async.md - read when using
AsyncPplxClientorpplx_srch_sdk.utils.fanoutdirectly.
Runnable examples (each needs PERPLEXITY_API_KEY exported):
- examples/web_search.py - one-off web search with filters and typed error handling.
- examples/fanout_research.py - fan out queries, split successes from errors, dedup, write JSONL artifacts.
- examples/snippets_verify.py - search, then verify hits with query-relevant snippets.
- examples/async_client.py - async client with bounded custom fan-out.