# `Perplexed`
[🔗](https://github.com/agoodway/perplexed/blob/v0.2.0/lib/perplexed.ex#L1)

A small web search, scrape and research toolkit, in the spirit of Firecrawl
or Exa.

  * `search/2` runs one web search on a chosen provider and returns a
    `Perplexed.Search.Result` with the hits and the call's cost. With
    `scrape: true` it also scrapes each hit's page and attaches the
    `Perplexed.Document`.
  * `scrape/2` turns one URL into Markdown through a configurable ladder
    of scrapers (see `Perplexed.Scrape`).
  * `batch_scrape/2` scrapes several URLs concurrently under
    `Perplexed.TaskSupervisor`.
  * `answer/2` answers a question from the web in fixed steps (plan,
    search, rank, scrape, summarize) and returns the answer with its sources
    and every hit considered (see `Perplexed.Pipeline`).

Search providers and scrapers are adapters (`Perplexed.Search`,
`Perplexed.Scraper`), so any of them can be swapped for another built-in
or a custom module. Search and scrape need no model, so they work on their
own as a search engine with optional page contents. Every result carries
its USD cost.

# `error`

```elixir
@type error() :: {atom(), map()}
```

Every error Perplexed returns: a reason atom (such as `:invalid_request`,
`:authentication`, `:quota_exhausted`, `:rate_limited`, `:upstream`,
`:transport`, `:unreadable`) and a map of details.

# `model_spec`

```elixir
@type model_spec() :: String.t() | tuple() | map()
```

A ReqLLM model spec: `"provider:model"`, a tuple, or a model map or struct.

# `answer`

```elixir
@spec answer(String.t(), keyword()) :: {:ok, Perplexed.Answer.t()} | {:error, error()}
```

Answers `query` from the web in fixed steps (plan, search, rank, scrape,
summarize) and returns the answer with its sources and every hit
considered. See `Perplexed.Pipeline.run/2`.

# `batch_scrape`

```elixir
@spec batch_scrape([String.t()], keyword()) :: [
  {String.t(), Perplexed.Scrape.result()}
]
```

Scrapes URLs concurrently. See `Perplexed.Scrape.batch/2`.

# `child_spec`

```elixir
@spec child_spec(keyword()) :: Supervisor.child_spec()
```

Child spec that starts Perplexed with `settings` (see `Perplexed.Config`).
Add `{Perplexed, settings}` to the host application's supervision tree;
the settings are cleared when it stops. There is one instance per node.

The settings are handed over inside a function, so supervisor and crash
reports do not print the API keys they carry.

# `scrape`

```elixir
@spec scrape(String.t(), keyword()) :: Perplexed.Scrape.result()
```

Scrapes one URL to Markdown. Options are those of `Perplexed.Scrape.run/2`.

# `search`

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

Searches the web for `query`.

## Options

  * `:provider` - a `t:Perplexed.Search.spec/0`, default
    `Perplexed.Search.default_provider/0`.
  * `:limit` - results to return, default 8 (Cloudflare caps it at 10).
  * `:scrape` - also scrape each hit's page, default `false`. A hit whose
    page could not be read keeps `document: nil` and says why in
    `scrape_error`.
  * `:scrape_options` - options for `batch_scrape/2`, such as `:ladder`.

Other options go to the provider.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
