Python SDK
Install superartifacts, publish a page, read back what it collected, share it, and handle errors, retries, paging, uploads and async.
guide~4 minVerified v0.178.0
superartifacts needs Python 3.10 or newer. It is server side only:
keep the key on a server.
Install
pip install superartifacts
Create a key in Settings → API keys and put it in
SUPERARTIFACTS_API_KEY.
First call
import os
from superartifacts import SuperArtifacts
sa = SuperArtifacts(api_key=os.environ["SUPERARTIFACTS_API_KEY"])
print(sa.account.whoami()["handle"])
sa.artifacts.publish(
slug="hello",
prompt="A one-line hello page",
html="<!doctype html><h1>Hello</h1>",
)
for artifact in sa.artifacts.list()["artifacts"]:
print(artifact["slug"])
for row in sa.data.iter_artifact_rows(artifact="hello"):
print(row)api_key falls back to SUPERARTIFACTS_API_KEY, so SuperArtifacts() is enough.
Keyword arguments hold path, query and body fields together, in snake_case: values_file= goes out
as valuesFile. Values nested inside a dict keep the API's spelling. Answers are plain dicts, as
API endpoints describes them.
AsyncSuperArtifacts is the same surface, awaited.
import asyncio
from superartifacts import AsyncSuperArtifacts
async def main():
async with AsyncSuperArtifacts() as sa:
print(await sa.account.whoami())
async for row in sa.data.iter_artifact_rows(artifact="hello"):
print(row)
asyncio.run(main())Configuration
SuperArtifacts(
api_key=None, # or SUPERARTIFACTS_API_KEY
base_url="https://superartifacts.app",
timeout=60.0, # seconds
max_retries=2,
http_client=None, # your own httpx.Client; yours to close
)Every method also takes timeout= for that call, and every POST takes idempotency_key=. Use
the client as a context manager, or call close().
Publish a page
A page is private when it first publishes. Publishing the same slug again adds a new version at the same address.
page = sa.artifacts.publish(
slug="launch-notes",
title="Launch notes",
prompt="A page of launch notes.",
html="<h1>Launch notes</h1>",
)
print(page["url"])Collect data and read it back
Without a policy, a form stores nothing. This one is an answer sheet: one row per person. The
form in rsvp.html saves to it, as Collect responses describes.
from pathlib import Path
sa.artifacts.publish(
slug="rsvp",
prompt="An RSVP form.",
html=Path("rsvp.html").read_text(),
policy={
"policyVersion": 1,
"writes": "upsert",
"perUser": "single",
"read": "own",
"retentionDays": None,
"recordCap": {"perVisitor": 1, "perArtifact": 500},
"fields": [
{"name": "attending", "type": "boolean", "required": False, "pii": "none", "description": "Yes or no."},
],
},
)
for row in sa.data.iter_artifact_rows(artifact="rsvp"):
print(row)Share an artifact
sa.shares.add_to_artifact(
artifact="launch-notes",
emails=["dana@example.com"],
role="commenter",
)Each person gets an email. The role is viewer unless you say otherwise, and sharing endpoints
allow 20 calls an hour per key.
Errors
Everything raised is a SuperArtifactsError, with .status, .code, .message, .retry_after
and the full error object in .details. Error codes lists every code.
A client-side failure has .status 0 and its own .code: a missing key is an AuthenticationError
with missing_api_key.
| Status | Exception |
|---|---|
| 400 | InvalidArgumentsError |
| 401 | AuthenticationError (also raised when there is no key) |
| 402 | PlanLimitError |
| 403 | PermissionError, or ScopeRequiredError when a read key tried a write |
| 404 | NotFoundError |
| 409 | ConflictError |
| 413 | PayloadTooLargeError |
| 429 | RateLimitError |
| 5xx | InternalError |
| none | APIConnectionError, APITimeoutError |
from superartifacts import NotFoundError
try:
sa.artifacts.get(artifact="missing")
except NotFoundError as error:
print(error.code, error.message)superartifacts.PermissionError is not the builtin of that name: import it by name when you catch
it.
Retries and idempotency
Up to max_retries more tries, two by default:
- A
429is retried after itsRetry-After. Past 60 seconds it is not waited out: theRateLimitErrorcomes straight back withretry_afterset. 502,503,504and network errors are retried with exponential backoff and jitter, forGET,DELETEandPOST.PATCHandPUTare retried on429only.500and every other4xxare never retried.
Every POST sends an Idempotency-Key, one per call and the same across its retries, so a retried
publish happens once. Pass idempotency_key= to choose your own, which also makes a retry of your
own code safe.
Pagination
iter_* methods walk every page and yield one item at a time, taking the same arguments as the
list they wrap.
for row in sa.data.iter_rows(dataset="signups"):
print(row)
for row in sa.data.iter_artifact_rows(artifact="hello"):
print(row)
for entry in sa.entries.iter(collection="posts"):
print(entry)
for mention in sa.mentions.iter():
print(mention)Uploads
A JSON body stops at 1 MiB. uploads.upload stages bigger files and sends the bytes straight to
their upload URLs. It takes a mapping of path in the artifact to bytes or a local file, within 5 MiB
a file and 10 MiB a set.
staged = sa.uploads.upload({"images/hero.png": "./hero.png", "notes.txt": b"hello"})
sa.artifacts.publish(
slug="hello",
prompt="A page with a picture",
html='<!doctype html><img src="images/hero.png">',
uploads=staged["uploadId"],
)Every method
| Method | Does |
|---|---|
sa.account.whoami | Who this key publishes as |
sa.account.help | How the operations fit together |
sa.artifacts.list | List your artifacts, newest first |
sa.artifacts.publish | Publish an artifact, or a new version of one |
sa.artifacts.get | One artifact: its versions, published version and declaration |
sa.artifacts.update | Change visibility, gallery label, live phase or submission email |
sa.artifacts.delete | Delete an artifact with every version, permanently |
sa.artifacts.read_file | Read one file of a version as text (default: the published index.html) |
sa.artifacts.open | A ten-minute viewer URL for an artifact, private included |
sa.artifacts.serve_version | Serve an existing version again |
sa.live.start | Turn an artifact's live room on |
sa.live.stop | Turn an artifact's live room off, disconnecting every visitor |
sa.uploads.stage | Stage big files for a publish; PUT each to its URL |
sa.uploads.upload | Stage files and send their bytes |
sa.data.list_artifact_rows | Rows visitors submitted to one artifact, newest first |
sa.data.iter_artifact_rows | Every row of one artifact, page by page |
sa.data.write_artifact_rows | Write rows into an artifact you may edit |
sa.data.list_rows | Rows submitted to a dataset or a gallery label |
sa.data.iter_rows | Every row of a dataset or label, page by page |
sa.data.query | Aggregate what was collected: numbers, never rows |
sa.schema.get | The data catalog: datasets and content collections, or a comparison |
sa.schema.define_collection | Create or replace a content collection |
sa.entries.list | One page of a content collection's entries |
sa.entries.iter | Every entry, page by page |
sa.entries.create | Create one entry, or a batch |
sa.secrets.use | Call an approved API with a stored secret you cannot read (workspace keys) |
sa.templates.list | Find, rank or list templates |
sa.templates.save | Create or revise a template draft |
sa.templates.get | One template in full, with its install prompt |
sa.templates.get_history | A template's changelog |
sa.templates.get_feedback | Comments on a template's artifacts, as themes |
sa.templates.act | Install, uninstall, rate, report, roll back or request verification |
sa.templates.release | Release one version of your template |
sa.projects.list | Your projects |
sa.projects.create | Create a project, optionally seeded with artifacts |
sa.projects.get | One project and its items |
sa.projects.update | Rename, set visibility, reorder, move, fold groups or set a rule |
sa.projects.delete | Delete a project; its artifacts stay |
sa.projects.add_artifacts | Add artifacts to a project |
sa.projects.remove_artifact | Remove one artifact (or item) from a project |
sa.shares.add_to_artifact | Give named people access to an artifact |
sa.shares.add_to_project | Give named people access to a project |
sa.shares.add_to_workspace | Invite editors to a workspace (returns accept links) |
sa.comments.list | Comment threads on an artifact, newest first |
sa.comments.respond | Reply to and/or resolve threads, one outcome each |
sa.comments.get_screenshot | One message's screenshot, base64 in `image` |
sa.mentions.list | Where people @mentioned you; listing marks them seen |
sa.mentions.iter | Every mention, page by page |
sa.signatures.list | An artifact's signature requests, newest first |
sa.signatures.request | Ask named people to sign one version of an artifact |
sa.signatures.get | One signature request and its signers |
sa.signatures.remind | Email a signer their link again |
sa.signatures.withdraw | Close an open signature request |