Perplexed.Search behaviour (Perplexed v0.2.0)

Copy Markdown View Source

Swappable web search: every provider takes a query and returns normalized Perplexed.Search.Hits, so callers change engines by changing one option. Built-in providers, by name, with list price per successful request (USD, as of 2026-11-10) used for per-run cost accounting:

NameRoutePer request
:ceramicCloudflare Web Search, Ceramic$0.00025
:deepseekDeepSeek Search API$0.00075
:firecrawlFirecrawl search, 2 Standard credits$0.00198
:linkupCloudflare Web Search, Linkup fast$0.005
:cloudflare_exaCloudflare Web Search, Exa auto$0.007
:searxngYour SearXNG instances, keylessfree

Firecrawl bills in credits, so its price follows the firecrawl_credit_usd setting (default the Standard plan rate).

Any module implementing this behaviour can be passed instead, as Module or {Module, opts}; its optional price/1 and free_when_empty?/0 feed cost accounting.

Summary

Callbacks

Whether the provider does not bill a request that returns no hits. Optional; defaults to false, so an empty result costs the list price.

List price in USD of one successful request made with opts (the spec's options). Optional; defaults to 0.

Functions

The provider used when a call names none: config :perplexed, provider:, else :ceramic.

The name a provider is reported by in results, attempts and telemetry: a built-in name or the module, never a {Module, opts} spec's options, which can hold credentials. A fallback chain is :chain.

Price in USD of one successful request to provider: the config :perplexed, prices: override for that spec when set, else the provider's list price (0 for a provider without price/1).

The built-in provider names.

Searches query on provider and returns the hits with the call's cost.

Types

spec()

@type spec() :: atom() | module() | {module(), keyword()} | [spec(), ...]

Callbacks

free_when_empty?()

(optional)
@callback free_when_empty?() :: boolean()

Whether the provider does not bill a request that returns no hits. Optional; defaults to false, so an empty result costs the list price.

price(keyword)

(optional)
@callback price(keyword()) :: float()

List price in USD of one successful request made with opts (the spec's options). Optional; defaults to 0.

search(t, keyword)

@callback search(String.t(), keyword()) ::
  {:ok, [Perplexed.Search.Hit.t()]} | {:error, term()}

Functions

default_provider()

@spec default_provider() :: spec()

The provider used when a call names none: config :perplexed, provider:, else :ceramic.

name(name)

@spec name(spec()) :: atom()

The name a provider is reported by in results, attempts and telemetry: a built-in name or the module, never a {Module, opts} spec's options, which can hold credentials. A fallback chain is :chain.

price(provider)

@spec price(spec()) :: float()

Price in USD of one successful request to provider: the config :perplexed, prices: override for that spec when set, else the provider's list price (0 for a provider without price/1).

providers()

@spec providers() :: [atom()]

The built-in provider names.

run(provider, query, opts \\ [])

@spec run(spec(), String.t(), keyword()) ::
  {:ok, Perplexed.Search.Result.t()} | {:error, Perplexed.error()}

Searches query on provider and returns the hits with the call's cost.

:limit (default 8) is common to the built-in providers; Cloudflare caps it at 10. :credentials (a keyword list of setting keys, such as firecrawl_api_key:) overrides the configured credentials for this call. :req_options is merged into each built-in provider's Req call last, so tests can pass plug:. Other options go to the provider.

An unknown provider is {:error, {:invalid_request, %{reason: {:unknown_provider, spec}}}}.

A list of specs is a fallback chain, tried in order until one answers, with per-provider and overall time limits (:provider_timeout_ms, :deadline_ms, :fallback_on); see Perplexed.Search.Fallback.