anakin-elixir

Official Elixir SDK for Anakin — 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:

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

Then:

mix deps.get

Requires Elixir 1.14+.

Quickstart

{: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

{: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:

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:

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

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

Related packages

License

Apache 2.0