Cross-user search
Reading across every user in the app: listing users, searching them, and sweeping their records. This is what Python is usually here for — exports, backfills, admin tooling and scheduled jobs.
Secret key only
Every method here reads across user boundaries, so every one requires a secret key and raises InsufficientScopeError (403) without it. None of these calls belong anywhere a user could reach them.
get_all_users_from_appAPI key only
GET /api/app/users
authix.get_all_users_from_app(
*, limit: int | None = None, cursor: str | None = None,
) -> Page| Param | Type | Required | Description |
|---|---|---|---|
| limit | int | No | 1–100. Defaults to 20. |
| cursor | str | No | next_cursor from the previous page. |
Returns: Page
1page = authix.get_all_users_from_app(limit=100)
2
3for user in page:
4 print(user["email"])Newest first.
iter_all_usersAPI key only
GET /api/app/users (paged)
authix.iter_all_users(
*, limit: int | None = None, max_pages: int | None = None,
) -> Iterator[dict]| Param | Type | Required | Description |
|---|---|---|---|
| limit | int | No | Page size. Use 100 for a full sweep — fewer round trips, same total records. |
| max_pages | int | No | Bound the traversal. |
Returns: Iterator[dict]
1for user in authix.iter_all_users(limit=100):
2 process(user)search_in_all_app_usersAPI key only
GET /api/app/users/search
authix.search_in_all_app_users(
*, q: str | None = None, keys: dict | None = None,
limit: int | None = None, cursor: str | None = None,
) -> Page| Param | Type | Required | Description |
|---|---|---|---|
| q | str | No | Free-text match across searchable user fields. |
| keys | dict | No | Exact-match filter, e.g. {'isVerified': True}. |
| limit | int | No | 1–100. |
| cursor | str | No | For the next page. |
Returns: Page
1page = authix.search_in_all_app_users(
2 q="example.com",
3 keys={"isVerified": True},
4 limit=100,
5)Throws: ValidationError (400) when filtering on a field that is not searchable — the allowlist exists so a filter cannot be used to probe arbitrary columns.
get_all_users_data_from_appAPI key only
GET /api/app/users/data
authix.get_all_users_data_from_app(
*, category: str | None = None,
limit: int | None = None, cursor: str | None = None,
) -> Page| Param | Type | Required | Description |
|---|---|---|---|
| category | str | No | Restrict to one category. |
| limit | int | No | 1–100. |
| cursor | str | No | For the next page. |
Returns: Page
1page = authix.get_all_users_data_from_app(category="orders", limit=100)Each record carries the userId of its owner, which is what makes a write-back after a sweep possible.
search_in_all_app_users_dataAPI key only
GET /api/app/users/data/search
authix.search_in_all_app_users_data(
*, q: str | None = None, category: str | None = None,
keys: dict | None = None,
limit: int | None = None, cursor: str | None = None,
) -> Page| Param | Type | Required | Description |
|---|---|---|---|
| q | str | No | Free-text match. |
| category | str | No | Restrict to one category. |
| keys | dict | No | Exact-match filter on payload fields. |
| limit | int | No | 1–100. |
| cursor | str | No | For the next page. |
Returns: Page
1page = authix.search_in_all_app_users_data(
2 category="orders",
3 keys={"status": "refunded"},
4 limit=100,
5)iter_all_users_dataAPI key only
GET /api/app/users/data (paged)
authix.iter_all_users_data(
*, category: str | None = None, q: str | None = None,
keys: dict | None = None,
limit: int | None = None, max_pages: int | None = None,
) -> Iterator[dict]| Param | Type | Required | Description |
|---|---|---|---|
| category | str | No | Restrict to one category. |
| q | str | No | Free-text match. Passing q or keys switches to the search endpoint. |
| keys | dict | No | Exact-match filter. |
| limit | int | No | Page size. |
| max_pages | int | No | Bound the traversal. |
Returns: Iterator[dict]
1for row in authix.iter_all_users_data(q="invoice", max_pages=10):
2 process(row)Lists or searches depending on whether q/keys are given, so one loop covers both.
Exporting every user to CSV
1import csv
2from neuctra_authix import Authix
3
4authix = Authix(app_id=APP_ID, secret_key=SECRET_KEY)
5
6# iter_* is a generator, so a 50,000-user export never holds more than
7# one page in memory — the row is written and discarded.
8with open("users.csv", "w", newline="") as fh:
9 writer = csv.DictWriter(fh, fieldnames=["id", "email", "name", "createdAt"])
10 writer.writeheader()
11
12 for user in authix.iter_all_users(limit=100):
13 writer.writerow({k: user.get(k) for k in writer.fieldnames})Why a generator matters here
Building a list of 50,000 users before writing the file would hold the entire dataset in memory. Iterating writes each row and discards it, so peak memory is one page regardless of account size.
Backfilling a field
1# A backfill is the classic Python job: read every record across every
2# user, transform it, write it back. Bound it and log progress — a sweep
3# that silently stops halfway is worse than one that fails loudly.
4processed = 0
5
6for record in authix.iter_all_users_data(category="orders", limit=100):
7 if record.get("currency"):
8 continue
9
10 authix.update_user_data(
11 user_id=record["userId"],
12 data_id=record["id"],
13 data={"currency": "USD"},
14 version=record["version"],
15 )
16
17 processed += 1
18 if processed % 500 == 0:
19 print(f"{processed} records updated")Mind the request quota
A sweep that reads and writes every record consumes two requests per record. On a large account that alone can exhaust a monthly quota — check your plan limit before starting, and use max_pages to test the job on a slice first.
Writing back with version is also worth the extra care: a backfill running while users are active will hit conflicts, and a 409 you ignore is a record you silently skipped.
Related