Types & errors
Everything the package exports: the client, the Page object, the key helpers, and the exception hierarchy that lets you catch the one case you handle and let the rest surface.
Everything exported
1from neuctra_authix import (
2 # client
3 Authix, # the client
4 NeuctraAuthix, # alias, matching the JavaScript SDK's class name
5 DEFAULT_BASE_URL,
6
7 # pagination
8 Page, MAX_LIMIT, DEFAULT_LIMIT,
9
10 # keys
11 parse_api_key, mask_key, ParsedKey, PUBLISHABLE, SECRET,
12
13 # errors
14 AuthixError, # base of everything below
15 ConfigurationError, # bad client setup — raised before any request
16 NetworkError, # request never completed
17 AuthixAPIError, # base for every API error response
18 AuthenticationError, # 401
19 NoUserSessionError, # 401, no signed-in user
20 InsufficientScopeError, # 403, wrong key type
21 PermissionDeniedError, # 403, not permitted
22 NotFoundError, # 404
23 ValidationError, # 400
24 VersionConflictError, # 409
25 RateLimitError, # 429
26 ServerError, # 5xx
27)All of it lives at the package root. The submodules — client, pagination, keys, errors — are an implementation detail and may move.
The Page object
1page = authix.get_user_data(user_id=user_id, limit=50)
2
3len(page) # records in this page
4bool(page) # False when the page is empty
5page[0] # index into the records
6for r in page: ... # iterate them
7
8page.data # list[dict] — the records
9page.has_more # bool
10page.next_cursor # str | None — pass back as `cursor`
11page.total # records in THIS page, not the total that exist
12page.raw # the untouched response, if you need a field we do not expose| Attribute | Type | Notes |
|---|---|---|
| data | list[dict] | The records in this page. |
| has_more | bool | Whether another page exists. |
| next_cursor | str | None | Pass back as cursor to fetch the next page. |
| total | int | Records in this page. List endpoints report totalFetched and search endpoints totalItems; both are normalised here. |
| raw | dict | The untouched response payload. |
total is not a count of everything
It is how many records came back in this page. There is no cheap count of everything, because counting would mean scanning what pagination exists to avoid. If you need a total, maintain a counter as you write.
| Constant | Value | Notes |
|---|---|---|
| DEFAULT_LIMIT | 20 | Applied when limit is omitted. |
| MAX_LIMIT | 100 | A larger limit is clamped, not rejected. |
Exception hierarchy
1AuthixError # catch this to catch everything
2├── ConfigurationError # bad key, missing app_id — no request was made
3├── NetworkError # timeout, DNS, connection refused
4└── AuthixAPIError # the API returned an error
5 ├── AuthenticationError 401
6 │ └── NoUserSessionError 401 — endpoint needs a signed-in user
7 ├── InsufficientScopeError 403 — publishable key on a secret route
8 ├── PermissionDeniedError 403 — unverified account, plan limit
9 ├── NotFoundError 404
10 ├── ValidationError 400
11 ├── VersionConflictError 409 — carries current_version
12 ├── RateLimitError 429 — carries reset_date
13 └── ServerError 5xxThe tree is the useful part: catching AuthenticationError also catches NoUserSessionError, and catching AuthixAPIError catches every API failure while still letting a NetworkError through — which you usually want, because the two mean different things.
Exception reference
| Exception | Status | What it means and what to do |
|---|---|---|
| ConfigurationError | — | Bad or missing key, missing app_id, both key types passed. Raised at construction, before any request. Fix the setup. |
| NetworkError | — | Timeout, DNS failure, connection refused. The request may never have arrived, or may have succeeded with the response lost. Treat as unknown, not failed — never blind-retry a write. |
| ValidationError | 400 | Rejected as invalid, including filtering on a non-searchable field. Fix the request. |
| AuthenticationError | 401 | Wrong password, or an expired or revoked credential. Show a sign-in error. |
| NoUserSessionError | 401 | The endpoint acts on the signed-in user and nobody is signed in. Send them to login. |
| InsufficientScopeError | 403 | A publishable key on a secret-key endpoint. This is a bug in your integration — move the call to your server. Carries hint. |
| PermissionDeniedError | 403 | Authenticated but not permitted — usually an unverified account or a plan limit. The message says which. |
| NotFoundError | 404 | The app, user or record does not exist under this account. Often a stale id. |
| VersionConflictError | 409 | Someone wrote first; nothing was saved. Carries current_version and expected_version. Re-read, reapply, retry. |
| RateLimitError | 429 | Rate limited, or the monthly quota is exhausted. Carries reset_date — back off until then. |
| ServerError | 5xx | The API failed. Retry with backoff, but only idempotent calls. |
Every AuthixAPIError also carries status, code, message and the raw payload. Branch on code, never on message text — wording changes, codes are the contract.
Handling them
1from neuctra_authix import (
2 InsufficientScopeError,
3 NetworkError,
4 NoUserSessionError,
5 RateLimitError,
6 VersionConflictError,
7)
8
9try:
10 authix.update_user_data(
11 user_id=user_id, data_id=data_id, data=changes, version=version,
12 )
13
14except VersionConflictError as exc:
15 # The only error routinely worth retrying. Nothing was written.
16 retry_from(exc.current_version)
17
18except NoUserSessionError:
19 # The session expired or was revoked mid-use.
20 send_to_login()
21
22except InsufficientScopeError:
23 # Our bug, not the user's: this call needs a secret key and belongs
24 # on the server. Log it loudly; never show it to a user.
25 logger.exception("wrong key type for this endpoint")
26 raise
27
28except RateLimitError as exc:
29 # Back off until the quota resets.
30 schedule_retry(after=exc.reset_date)
31
32except NetworkError:
33 # The request may never have arrived — or may have been processed
34 # with the response lost. Treat it as unknown, not as failed.
35 mark_unknown()Do not catch and continue
A swallowed 403 on a write looks identical to a successful save until someone refreshes. If you catch an exception, either handle it or re-raise it — except AuthixError: pass is how silent data loss gets shipped.
Do not forward InsufficientScopeError to users
It describes your server's configuration, not their request. It means your code used the wrong key. Log it as a deployment bug and show the user something generic.
Key helpers
1from neuctra_authix import mask_key, parse_api_key, SECRET
2
3parsed = parse_api_key(os.environ["AUTHIX_SECRET_KEY"])
4
5parsed.type # "secret"
6parsed.env # "live"
7parsed.prefix # "sk_live_a1b2c3d4" — not secret, safe to log
8
9if parsed.type == SECRET and running_on_client:
10 raise RuntimeError("secret key must not ship to end users")
11
12# Safe to print anywhere.
13print(mask_key(key)) # sk_live_a1b2c3d4…9f2c| Export | Type | Notes |
|---|---|---|
| parse_api_key | (str | None) -> ParsedKey | None | Parses without contacting the API. Returns None if missing or malformed. |
| mask_key | (str) -> str | Prefix plus the last four characters, e.g. sk_live_a1b2c3d4…9f2c. Safe for logs. |
| ParsedKey | NamedTuple | Fields: type, env, prefix — all non-sensitive. |
| PUBLISHABLE | "publishable" | Compare against ParsedKey.type. |
| SECRET | "secret" | Compare against ParsedKey.type. |
The prefix is not a secret
ParsedKey.prefix is the public lookup handle embedded in the key. It identifies which key was used without exposing it, which makes it the right thing to log when you want to trace a request back to a credential.
Client attributes
| Attribute | Type | Notes |
|---|---|---|
| app_id | str | The app every request is scoped to. |
| key_type | "publishable" | "secret" | Which credential this client holds. Useful for an assertion at startup. |
| key_prefix | str | Non-sensitive key prefix, safe to log. |
| base_url | str | API origin, without a trailing slash. |
| timeout | float | Per-request timeout in seconds. |
Related