Catalog PyPI
PACKAGE EXPLORERView in registry

abysshub

PyPI

The official Python library for the Abyss API.

Versions collected1
Direct dependencies2From release 0.1.1
Last observed20 Oct, 17:48 UTC

README

Version 0.1.1 documentation

abysshub

Pre-release: the Abyss API opens soon.

The official Python library for the Abyss API.

from abysshub import Abyss, AbyssError

abyss = Abyss()  # reads ABYSS_API_KEY

try:
    run = abyss.run("widget_131", {"amount": 250000})
    print(run.result)
except AbyssError as error:
    print(error.code, error.param, error.request_id, error.run)

run() presses the Widget and waits for the run to end, however long it takes, then returns the succeeded run. A refusal or a failed run raises AbyssError.

It runs on Python 3.9+, with one dependency, httpx. From async code, use AsyncAbyss (see Async):

async with AsyncAbyss() as abyss:
    run = await abyss.run("widget_131", {"amount": 250000})

The client

abyss = Abyss(api_key=None, base_url=None, max_retries=2, timeout=None)
  • api_key: defaults to ABYSS_API_KEY. Without a key, Abyss() raises AbyssError with code == "invalid_api_key".
  • base_url: defaults to ABYSS_BASE_URL, else https://api.abysshub.com. A key starting abyss_sk_dev_ goes to https://api.dev.abysshub.com.
  • max_retries: how many times a request is sent again (see Retries). Defaults to 2.
  • timeout: the seconds every call waits at most, unless it sets its own (see Timeouts). None by default.

Every request sends Authorization: Bearer <key> and User-Agent: abysshub-python/<version> (python <version>).

abyss.close() closes its connections; with Abyss() as abyss: does it for you.

Running a Widget

run = abyss.run(widget, input, max_price=None, idempotency_key=None, timeout=None)
  • widget is widget_<id>, or an unlisted Widget's private token.
  • input is the Widget's Fields by name, as a dict (abyss.widgets.get(widget) lists them as input_schema). A file Field takes a file (see Files).
  • max_price: the most the run may cost, in Byssium. A higher price is refused with price_above_max, and nothing is charged.
  • idempotency_key: each call presses with a fresh UUID Idempotency-Key unless you give one. Retries send the same one, so a run is never made or charged twice.
  • timeout: the seconds to wait at most (see Timeouts).

The press holds until the run ends. When the hold ends early (a deploy cuts it, the network goes down, the API's holds are all taken, or an hour passes), run() re-attaches with GET <Location>?wait=true until the run ends. Re-attaching has no limit and is not a retry. Connecting has 10 s, and 30 s of silence counts as a cut.

A run is a dataclass with /v1's fields: id, widget, status (queued, running, succeeded or failed), price, result (the Widget's JSON, a dict when it wrote an object), output_files, error, created_at, started_at and ended_at.

The routes

Under run() there is one method per route. Each also takes timeout= as a keyword.

MethodRouteReturns
abyss.runs.create(widget, input, max_price=None, idempotency_key=None)POST /v1/widgets/{widget}/runs with "wait": falsethe Run, queued, at once
abyss.runs.get(id, wait=False)GET /v1/runs/{id}, with ?wait=true when waitthe Run
abyss.runs.list(limit=None, starting_after=None)GET /v1/runs?limit=&starting_after=a RunList: data, has_more, newest first
abyss.widgets.get(widget)GET /v1/widgets/{widget}a Widget: id, name, description, url, price, free_runs, input_schema
abyss.uploads.create(widget, field, filename, size=None)POST /v1/widgets/{widget}/uploadsan Upload: id, upload.url, upload.fields
abyss.key()GET /v1/keya Key: name, key, user, spend_cap, spent_this_month, created_at

Each is a dataclass with /v1's fields; free_runs is a FreeRuns (limit, remaining) or None, and input_schema stays a dict.

queued = abyss.runs.create("widget_131", {"amount": 250000})
# … later, or in another process:
run = abyss.runs.get(queued.id, wait=True)

page = abyss.runs.list(limit=100)
while page.has_more:
    page = abyss.runs.list(limit=100, starting_after=page.data[-1].id)
  • runs.create() uploads the input's files like run(), then returns without waiting.
  • runs.get() returns the run as it is, failed or not: only run() raises a failed run. With wait=True, it holds until the run ends, reading again when the hold is cut. A hold answers after an hour, or at once when the API's holds are all taken, so read again while status is queued or running.
  • runs.list() returns each of data as a Run, with save() and output_files.
  • uploads.create() only asks for the grant. To upload by hand, POST every upload.fields entry, then the file as file, to upload.url, and put id in the input.

Files

from abysshub import Abyss, file

run = abyss.run("widget_1660", {
    "report": file("report.pdf"),
    "data": [file("jan.csv"), file(csv_bytes, filename="feb.csv"), "https://example.com/sales/mar.csv"],
})
paths = run.save("output")  # [Path("output/charts/a.png"), Path("output/output.json")]
  • file(path) is a file on disk, read when it is uploaded. A file Field also takes a Path, an open file (open("report.pdf", "rb")) or bytes. file(data, filename=...) names data. A file without a name uploads under its Field's name.
  • A string passes through as an https URL or an upload id. A multi-file Field takes a list, which may mix all three kinds.
  • Before the press, each file is uploaded with POST /v1/widgets/{widget}/uploads, then sent to storage, in parallel, and its id goes in the input. An upload refusal (held_input_*) raises before any press. A press that drops before any headers is sent again with the same ids; it never uploads again.
  • run.save(dir) writes every output file under dir at its path, making folders, and returns the paths it wrote.
  • Each of run.output_files has f.save(path) and f.read(), which returns bytes. An expired URL is refreshed once, by reading the run again with GET /v1/runs/{id}. A file of 64 MiB or more downloads as 16 parallel byte ranges of its one URL.

Errors

A refusal, a failed run, a timeout and a lost connection all raise one class, AbyssError. There is no class per status: branch on code.

Field
code/v1's error code, a failed run's error.code, or a library-only code below
messagewhat to do, for a person
statusthe HTTP status of a refusal, else None
paramthe Field, body key, query key or header at fault, else None
doc_urlthe code on the API access page, else None
request_idquote it to support, else None
shortfallwith insufficient_funds: how much Byssium the wallet is short, else None
pricewith price_above_max: what the run costs now, else None
run_idthe id of the run the call started or read, once the press's Location or a run body named it, else None
runthe run, for a failed run or a timeout, else None; an AsyncRun from AsyncAbyss
  • Refusals: invalid_api_key (401), not_found (404), invalid_request, invalid_input, held_input_*, input_unreachable and idempotency_key_reused (422), insufficient_funds (402), spend_cap_reached and price_above_max (400), idempotency_key_in_use (409), rate_limited (429), server_error (500).
  • A failed run: widget_fault, platform_fault or budget_exceeded, with the run as run.
  • Library-only codes: timeout (see Timeouts); connection_error, when the connection still drops before any answer after the retries, with the cause as __cause__; and unexpected_response, when an answer is not what the route sends.

Retries

A request is sent again, at most max_retries times (2 by default), only after:

  • a drop before any headers arrived: a short backoff, then the same request, with the same Idempotency-Key for a press;
  • 429 rate_limited and 409 idempotency_key_in_use: after the Retry-After seconds;
  • a 5xx: a short backoff (0.5 s, then 1 s).

When the last try fails too, it raises. Every other refusal raises at once.

Re-attaching never counts as a retry. A re-attach that drops, or meets a 429, 409 or 5xx, is tried again with no limit until the network is back, pausing 0.5 s and doubling to at most 10 s (or the Retry-After seconds). Only timeout stops it.

Timeouts

There is no timeout by default: run() waits as long as the run takes. timeout (seconds), on the client or on a call, stops the waiting, never the run.

try:
    run = abyss.run("widget_131", input, timeout=60)
except AbyssError as error:
    if error.code == "timeout" and error.run_id:
        # The run goes on: wait for it again, without pressing (and paying) twice.
        run = abyss.runs.get(error.run_id, wait=True)

It raises AbyssError with code == "timeout", run, the run as last seen or None, and run_id, the run's id. The press answers its Location at once, so run_id is set as soon as the press has answered, even while no run has been seen yet; it is None only when the waiting stopped before that. Nothing is sent after the waiting stops.

Async

AsyncAbyss is Abyss on httpx's async client. It takes the same options and has the same methods, each awaited; its retries, re-attaching and timeout behave the same.

from abysshub import AsyncAbyss, file

async with AsyncAbyss() as abyss:  # or abyss = AsyncAbyss(), then await abyss.close()
    run = await abyss.run("widget_1660", {"report": file("report.pdf")})
    paths = await run.save("output")
    queued = await abyss.runs.create("widget_131", {"amount": 250000})
    run = await abyss.runs.get(queued.id, wait=True)
  • Runs: it returns an AsyncRun, with the same fields as Run. Its save(dir) and each of its output_files' save(path) and read() are awaited. runs.list() returns an AsyncRunList, and an AbyssError raised by AsyncAbyss carries an AsyncRun as run.
  • Files are uploaded as concurrent tasks, each read from disk in a thread.
  • timeout= stops the waiting, as with Abyss. Cancelling the task stops it too, and raises CancelledError, not AbyssError.

Names

Returned data keeps /v1's names: output_files, has_more, request_id. The library's own options are snake_case: max_price, starting_after. A Field's name is never converted: input is sent as you write it.

Downloads & popularity

220downloads
1-day window · source-reported counts19 Oct 2026
9284640
2026-10-05: 928 · observed 2026-10-20T20:04:30.6583575Z · period 2026-10-05–2026-10-052026-10-06: 427 · observed 2026-10-20T20:04:30.6583575Z · period 2026-10-06–2026-10-062026-10-07: 28 · observed 2026-10-20T20:04:30.6583575Z · period 2026-10-07–2026-10-072026-10-08: 408 · observed 2026-10-20T20:04:30.6583575Z · period 2026-10-08–2026-10-082026-10-09: 39 · observed 2026-10-20T20:04:30.6583575Z · period 2026-10-09–2026-10-092026-10-10: 54 · observed 2026-10-20T20:04:30.6583575Z · period 2026-10-10–2026-10-102026-10-11: 8 · observed 2026-10-20T20:04:30.6583575Z · period 2026-10-11–2026-10-112026-10-12: 11 · observed 2026-10-20T20:04:30.6583575Z · period 2026-10-12–2026-10-122026-10-13: 2 · observed 2026-10-20T20:04:30.6583575Z · period 2026-10-13–2026-10-132026-10-14: 10 · observed 2026-10-20T20:04:30.6583575Z · period 2026-10-14–2026-10-142026-10-15: 46 · observed 2026-10-20T20:04:30.6583575Z · period 2026-10-15–2026-10-152026-10-16: 116 · observed 2026-10-20T20:04:30.6583575Z · period 2026-10-16–2026-10-162026-10-17: 247 · observed 2026-10-20T20:04:30.6583575Z · period 2026-10-17–2026-10-172026-10-18: 113 · observed 2026-10-20T20:04:30.6583575Z · period 2026-10-18–2026-10-182026-10-19: 220 · observed 2026-10-20T20:04:30.6583575Z · period 2026-10-19–2026-10-19
5 Oct19 Oct

pypistats · 15 observations shown. Missing days are unknown. Source category: overall: without mirrors.

View observed values
Date / source periodValueObserved at
2026-10-19 – 2026-10-192202026-10-20T20:04:30.6583575Z
2026-10-18 – 2026-10-181132026-10-20T20:04:30.6583575Z
2026-10-17 – 2026-10-172472026-10-20T20:04:30.6583575Z
2026-10-16 – 2026-10-161162026-10-20T20:04:30.6583575Z
2026-10-15 – 2026-10-15462026-10-20T20:04:30.6583575Z
2026-10-14 – 2026-10-14102026-10-20T20:04:30.6583575Z
2026-10-13 – 2026-10-1322026-10-20T20:04:30.6583575Z
2026-10-12 – 2026-10-12112026-10-20T20:04:30.6583575Z
2026-10-11 – 2026-10-1182026-10-20T20:04:30.6583575Z
2026-10-10 – 2026-10-10542026-10-20T20:04:30.6583575Z
2026-10-09 – 2026-10-09392026-10-20T20:04:30.6583575Z
2026-10-08 – 2026-10-084082026-10-20T20:04:30.6583575Z
2026-10-07 – 2026-10-07282026-10-20T20:04:30.6583575Z
2026-10-06 – 2026-10-064272026-10-20T20:04:30.6583575Z
2026-10-05 – 2026-10-059282026-10-20T20:04:30.6583575Z

Versions 1

Collected releases
VersionPublishedDirect dependenciesOpen version
0.1.18 Oct 20262

Package advisories

0
No matching advisories collected

This describes our collected data; it is not a security assessment.

Repository

PENDING
abysshub/abysshub-sdk

Shared repository data · GitHub

License detected in repository
Not collected

Metadata history

Meaningful changes
Current collected state Current

Observed since 20 Oct, 17:48 UTC

No superseded metadata states collected. Unchanged observations do not create new revisions.

Record identity

package:228b2c859b172558a9b00655f3a93bee172f6c8998abfab929c38da273e5a157