README.md

# anakin-elixir

Official Elixir SDK for [Anakin](https://anakin.io) — web scraping, crawling, search, and Wire actions.

> **Status: alpha (v0.1.x).** Public API may change between minor versions until v1.0.

## Install

Add to your `mix.exs`:

```elixir
def deps do
  [{:anakin, "~> 0.1"}]
end
```

Then:

```bash
mix deps.get
```

Requires Elixir 1.14+.

## Quickstart

```elixir
{:ok, client} = Anakin.Client.new(api_key: "ak-...")  # or set ANAKIN_API_KEY

case Anakin.scrape(client, "https://example.com") do
  {:ok, doc} -> IO.puts(doc["markdown"])
  {:error, e} -> IO.warn(Exception.message(e))
end
```

The SDK polls long-running jobs internally — `Anakin.scrape/3` and friends block until the job reaches a terminal status, then return `{:ok, result}`. No job IDs to manage, no polling loops to write.

## Function overview

| Function | Endpoint | Sync? |
|---|---|---|
| `Anakin.scrape(client, url, opts \\ [])` | `POST /url-scraper` → poll | async |
| `Anakin.map(client, url, opts \\ [])` | `POST /map` → poll | async |
| `Anakin.crawl(client, url, opts \\ [])` | `POST /crawl` → poll | async |
| `Anakin.search(client, query, opts \\ [])` | `POST /search` | sync |
| `Anakin.agentic_search(client, prompt, opts \\ [])` | `POST /agentic-search` → poll | async |
| `Anakin.wire(client, action_id, params \\ %{})` | `POST /holocron/task` → poll | async |
| `Anakin.list_sessions/1`, `create_session/3`, `save_session/3`, `update_session/3`, `delete_session/2` | `/browser-sessions/*` | various |
| `Anakin.Countries.supported/0` | offline (bundled) | sync |

## Configuration

```elixir
{:ok, client} = Anakin.Client.new(
  api_key: "ak-...",                      # or ANAKIN_API_KEY env var
  base_url: "https://api.anakin.io/v1",
  request_timeout_ms: 60_000,             # per-request timeout
  max_retries: 4,                         # on 429 / 5xx / transient
  poll_interval_ms: 1_000,                # initial polling delay
  poll_max_interval_ms: 10_000,           # capped backoff
  poll_timeout_ms: 300_000,               # total poll budget
  req_options: []                         # extra options forwarded to Req.request/2
)
```

Or, with `Anakin.Client.new!/1` to raise on missing key:

```elixir
client = Anakin.Client.new!(api_key: System.fetch_env!("ANAKIN_API_KEY"))
```

## Errors

Every error returned by the SDK is a struct that implements the `Exception` behaviour:

```elixir
case Anakin.scrape(client, "https://example.com") do
  {:ok, doc} -> doc
  {:error, %Anakin.Error.InsufficientCredits{balance: b, required: r}} ->
    IO.puts("out of credits: balance=#{b}, needed=#{r}")
  {:error, %Anakin.Error.Authentication{}} ->
    IO.puts("invalid API key — get a fresh one at anakin.io/dashboard")
  {:error, %Anakin.Error.RateLimit{retry_after: ra}} ->
    IO.puts("rate limited; retry after #{ra}s")
  {:error, %Anakin.Error.JobFailed{reason: reason}} ->
    IO.puts("job failed: #{reason}")
  {:error, e} ->
    IO.puts("unknown error: #{Exception.message(e)}")
end
```

The error hierarchy:

| Module | When |
|---|---|
| `Anakin.Error.Authentication` | 401 — invalid or missing API key |
| `Anakin.Error.InsufficientCredits` | 402 — out of credits (`:balance`, `:required`) |
| `Anakin.Error.InvalidRequest` | 400 — validation failure |
| `Anakin.Error.RateLimit` | 429 — after retry budget exhausted (`:retry_after` seconds) |
| `Anakin.Error.JobFailed` | Polled job came back with `status="failed"` (`:reason`) |
| `Anakin.Error.JobTimeout` | Polling budget exhausted before terminal status |
| `Anakin.Error.Server` | 5xx — after retries exhausted |
| `Anakin.Error.Network` | DNS / connect / read-timeout |
| `Anakin.Error` | Base struct; unmatched failures fall back here |

## Develop

```bash
git clone https://github.com/Anakin-Inc/anakin-elixir
cd anakin-elixir
mix deps.get
mix test
```

## Related packages

- [`@anakin-io/sdk`](https://www.npmjs.com/package/@anakin-io/sdk) — Node.js / TypeScript SDK
- [`anakin-sdk`](https://pypi.org/project/anakin-sdk/) — Python SDK
- [`github.com/Anakin-Inc/anakin-go`](https://github.com/Anakin-Inc/anakin-go) — Go SDK
- [`io.anakin:anakin-sdk`](https://search.maven.org/artifact/io.anakin/anakin-sdk) — Java SDK
- [`anakin-sdk` (rubygems)](https://rubygems.org/gems/anakin-sdk) — Ruby SDK
- [`Anakin` (NuGet)](https://www.nuget.org/packages/Anakin) — .NET SDK
- [`anakin-sdk` (crates.io)](https://crates.io/crates/anakin-sdk) — Rust SDK
- [`anakin/sdk` (Packagist)](https://packagist.org/packages/anakin/sdk) — PHP SDK
- [`@anakin-io/mcp`](https://www.npmjs.com/package/@anakin-io/mcp) — Model Context Protocol server for AI agents

## License

[Apache 2.0](LICENSE)