# `Kagi`
[🔗](https://github.com/rubas/kagi_ex/blob/v0.1.0/lib/kagi.ex#L1)

Typed client for [Kagi](https://kagi.com) Search, Summarizer, and Maps.

Configure `:session_token` before calling the one-off helpers, or build a
reusable `Kagi.Client` with `new/0`. Requests are built with `Req` and sent
through `CloakedReq`.

## Quick start

    config :kagi_ex, session_token: "..."
    client = Kagi.new!()
    {:ok, search} = Kagi.search(client, "elixir req", lens: :programming, limit: 5)
    {:ok, summary} = Kagi.summarize(client, "https://elixir-lang.org")
    {:ok, places} = Kagi.maps(client, "coffee zurich", ll: "47.3769,8.5417")

Non-bang functions return `{:ok, struct}` or `{:error, %Kagi.Error{}}`.
Bang functions return the struct or raise `Kagi.Error`.

# `query`

```elixir
@type query() :: String.t() | [String.t()]
```

Query argument accepted by `search/1..3` and `maps/1..3`.

Lists are joined with spaces before the request is made.

# `maps`

```elixir
@spec maps(query()) :: {:ok, Kagi.Maps.t()} | {:error, Kagi.Error.t()}
```

Searches Kagi Maps using application config and default Maps options.

# `maps`

```elixir
@spec maps(Kagi.Client.t(), query()) ::
  {:ok, Kagi.Maps.t()} | {:error, Kagi.Error.t()}
@spec maps(
  query(),
  keyword()
) :: {:ok, Kagi.Maps.t()} | {:error, Kagi.Error.t()}
```

Searches Kagi Maps with either a prebuilt client or application config.

`maps(client, query)` uses default Maps options. `maps(query, options)`
builds a client from application config and applies the supplied Maps
options.

# `maps`

```elixir
@spec maps(Kagi.Client.t(), query(), keyword()) ::
  {:ok, Kagi.Maps.t()} | {:error, Kagi.Error.t()}
```

Searches Kagi Maps for places matching a query.

Accepts a prebuilt `Kagi.Client`. Use `maps/2` or `maps/1` to build the
client from application config.

## Maps options

  * `:limit` - maximum result count (default `10`); applied client-side
    after sorting.
  * `:ll` - center coordinate as `"LAT,LON"` (e.g. `"47.3769,8.5417"`).
  * `:bbox` - bounding box as `"WEST,SOUTH,EAST,NORTH"`.
  * `:zoom` - zoom level as a number.
  * `:sort` - `:relevance` (server order), `:rating`, `:distance`, or
    `:price`. Price sorts by `$`-string length.
  * `:order` - `:asc` or `:desc`. Defaults are `:desc` for `:rating`,
    `:asc` for `:distance` and `:price`. `nil` values always sort last.

Sorting and the limit apply client-side, after the API response is parsed.

## Examples

    client = Kagi.new!()
    {:ok, %Kagi.Maps{results: results}} =
      Kagi.maps(client, "coffee zurich", ll: "47.3769,8.5417", sort: :rating)

# `maps!`

```elixir
@spec maps!(query()) :: Kagi.Maps.t()
```

Searches Kagi Maps with application config and raises `Kagi.Error` on failure.

# `maps!`

```elixir
@spec maps!(Kagi.Client.t(), query()) :: Kagi.Maps.t()
@spec maps!(
  query(),
  keyword()
) :: Kagi.Maps.t()
```

Searches Kagi Maps and raises `Kagi.Error` on failure.

# `maps!`

```elixir
@spec maps!(Kagi.Client.t(), query(), keyword()) :: Kagi.Maps.t()
```

Searches Kagi Maps and raises `Kagi.Error` on failure.

# `new`

```elixir
@spec new() :: {:ok, Kagi.Client.t()} | {:error, Kagi.Error.t()}
```

Builds a reusable `Kagi.Client`.

Reads `:session_token` and `:req_options` from application config. If the
session token is missing or invalid, returns
`{:error, %Kagi.Error{reason: :missing_session_token}}`.

## Application config

  * `:session_token` - Kagi session token string.
  * `:req_options` - keyword list merged into every `Req` request.

Returns `{:error, %Kagi.Error{reason: :invalid_option}}` when
`:req_options` is not a keyword list.

## Examples

    config :kagi_ex, session_token: "abc"
    {:ok, client} = Kagi.new()

# `new!`

```elixir
@spec new!() :: Kagi.Client.t()
```

Builds a reusable `Kagi.Client` or raises `Kagi.Error`.

# `search`

```elixir
@spec search(query()) :: {:ok, Kagi.Search.t()} | {:error, Kagi.Error.t()}
```

Searches Kagi using application config and default search options.

Requires `config :kagi_ex, :session_token, "..."` or returns `{:error,
%Kagi.Error{reason: :missing_session_token}}`.

# `search`

```elixir
@spec search(Kagi.Client.t(), query()) ::
  {:ok, Kagi.Search.t()} | {:error, Kagi.Error.t()}
@spec search(
  query(),
  keyword()
) :: {:ok, Kagi.Search.t()} | {:error, Kagi.Error.t()}
```

Searches Kagi with either a prebuilt client or application config.

`search(client, query)` uses default search options. `search(query, options)`
builds a client from application config and applies the supplied search
options.

# `search`

```elixir
@spec search(Kagi.Client.t(), query(), keyword()) ::
  {:ok, Kagi.Search.t()} | {:error, Kagi.Error.t()}
```

Searches Kagi and returns typed results.

Accepts a prebuilt `Kagi.Client`. Use `search/2` or `search/1` to build the
client from application config.

## Search options

  * `:limit` - maximum result count (default `10`); applied client-side.
  * `:region` - region code such as `"ch"`, `"us"`, `"de"`, or `"no_region"`.
  * `:lens` - `:default`, `:programming`, `:forums`, `:pdfs`,
    `:non_commercial`, or `:world_news`.
  * `:sort` - `:recency`, `:website`, or `:ad_trackers`.
  * `:time` - `:day`, `:week`, `:month`, or `:year`.
  * `:from` - start date as `YYYY-MM-DD`; cannot be combined with `:time`.
  * `:to` - end date as `YYYY-MM-DD`; cannot be combined with `:time`.
  * `:site` - appends a `site:` filter to the query.
  * `:filetype` - appends a `filetype:` filter to the query.
  * `:verbatim` - disables query expansion when `true`.

Returns `{:error, %Kagi.Error{}}` for invalid options, HTTP failures,
CAPTCHA/challenge pages, and parse failures.

## Examples

    client = Kagi.new!()
    {:ok, %Kagi.Search{results: results}} =
      Kagi.search(client, "elixir req http client", lens: :programming, limit: 3)

    Kagi.search("elixir lang", limit: 5)

# `search!`

```elixir
@spec search!(query()) :: Kagi.Search.t()
```

Searches Kagi with application config and raises `Kagi.Error` on failure.

# `search!`

```elixir
@spec search!(Kagi.Client.t(), query()) :: Kagi.Search.t()
@spec search!(
  query(),
  keyword()
) :: Kagi.Search.t()
```

Searches Kagi and raises `Kagi.Error` on failure.

# `search!`

```elixir
@spec search!(Kagi.Client.t(), query(), keyword()) :: Kagi.Search.t()
```

Searches Kagi and raises `Kagi.Error` on failure.

# `summarize`

```elixir
@spec summarize(String.t()) :: {:ok, Kagi.Summary.t()} | {:error, Kagi.Error.t()}
```

Summarizes a URL using application config and default summary options.

# `summarize`

```elixir
@spec summarize(Kagi.Client.t(), String.t()) ::
  {:ok, Kagi.Summary.t()} | {:error, Kagi.Error.t()}
@spec summarize(
  String.t(),
  keyword()
) :: {:ok, Kagi.Summary.t()} | {:error, Kagi.Error.t()}
```

Summarizes a URL with either a prebuilt client or application config.

`summarize(client, url)` uses default summary options. `summarize(url,
options)` builds a client from application config and applies the supplied
summary options.

# `summarize`

```elixir
@spec summarize(Kagi.Client.t(), String.t(), keyword()) ::
  {:ok, Kagi.Summary.t()} | {:error, Kagi.Error.t()}
```

Summarizes a single URL with Kagi Summarizer.

Accepts a prebuilt `Kagi.Client`. Use `summarize/2` or `summarize/1` to
build the client from application config.

## Summary options

  * `:type` - `:summary` (default) or `:takeaway`.
  * `:lang` - target language code, default `"EN"`.

Returns `{:error, %Kagi.Error{}}` for invalid options, HTTP failures,
and parse failures.

## Examples

    client = Kagi.new!()
    {:ok, %Kagi.Summary{summary: markdown}} =
      Kagi.summarize(client, "https://www.rust-lang.org/learn", type: :takeaway)

# `summarize!`

```elixir
@spec summarize!(String.t()) :: Kagi.Summary.t()
```

Summarizes a URL with application config and raises `Kagi.Error` on failure.

# `summarize!`

```elixir
@spec summarize!(Kagi.Client.t(), String.t()) :: Kagi.Summary.t()
@spec summarize!(
  String.t(),
  keyword()
) :: Kagi.Summary.t()
```

Summarizes a URL and raises `Kagi.Error` on failure.

# `summarize!`

```elixir
@spec summarize!(Kagi.Client.t(), String.t(), keyword()) :: Kagi.Summary.t()
```

Summarizes a URL and raises `Kagi.Error` on failure.

---

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