Skip to content

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.

Add the SDK with your coding agent
Your agent installs it, wires up the key and makes a first call.

Install

bash
pip install superartifacts

Create a key in Settings → API keys and put it in SUPERARTIFACTS_API_KEY.

First call

python
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.

python
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

python
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.

python
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.

python
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

python
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.

StatusException
400InvalidArgumentsError
401AuthenticationError (also raised when there is no key)
402PlanLimitError
403PermissionError, or ScopeRequiredError when a read key tried a write
404NotFoundError
409ConflictError
413PayloadTooLargeError
429RateLimitError
5xxInternalError
noneAPIConnectionError, APITimeoutError
python
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 429 is retried after its Retry-After. Past 60 seconds it is not waited out: the RateLimitError comes straight back with retry_after set.
  • 502, 503, 504 and network errors are retried with exponential backoff and jitter, for GET, DELETE and POST.
  • PATCH and PUT are retried on 429 only. 500 and every other 4xx are 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.

python
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.

python
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

MethodDoes
sa.account.whoamiWho this key publishes as
sa.account.helpHow the operations fit together
sa.artifacts.listList your artifacts, newest first
sa.artifacts.publishPublish an artifact, or a new version of one
sa.artifacts.getOne artifact: its versions, published version and declaration
sa.artifacts.updateChange visibility, gallery label, live phase or submission email
sa.artifacts.deleteDelete an artifact with every version, permanently
sa.artifacts.read_fileRead one file of a version as text (default: the published index.html)
sa.artifacts.openA ten-minute viewer URL for an artifact, private included
sa.artifacts.serve_versionServe an existing version again
sa.live.startTurn an artifact's live room on
sa.live.stopTurn an artifact's live room off, disconnecting every visitor
sa.uploads.stageStage big files for a publish; PUT each to its URL
sa.uploads.uploadStage files and send their bytes
sa.data.list_artifact_rowsRows visitors submitted to one artifact, newest first
sa.data.iter_artifact_rowsEvery row of one artifact, page by page
sa.data.write_artifact_rowsWrite rows into an artifact you may edit
sa.data.list_rowsRows submitted to a dataset or a gallery label
sa.data.iter_rowsEvery row of a dataset or label, page by page
sa.data.queryAggregate what was collected: numbers, never rows
sa.schema.getThe data catalog: datasets and content collections, or a comparison
sa.schema.define_collectionCreate or replace a content collection
sa.entries.listOne page of a content collection's entries
sa.entries.iterEvery entry, page by page
sa.entries.createCreate one entry, or a batch
sa.secrets.useCall an approved API with a stored secret you cannot read (workspace keys)
sa.templates.listFind, rank or list templates
sa.templates.saveCreate or revise a template draft
sa.templates.getOne template in full, with its install prompt
sa.templates.get_historyA template's changelog
sa.templates.get_feedbackComments on a template's artifacts, as themes
sa.templates.actInstall, uninstall, rate, report, roll back or request verification
sa.templates.releaseRelease one version of your template
sa.projects.listYour projects
sa.projects.createCreate a project, optionally seeded with artifacts
sa.projects.getOne project and its items
sa.projects.updateRename, set visibility, reorder, move, fold groups or set a rule
sa.projects.deleteDelete a project; its artifacts stay
sa.projects.add_artifactsAdd artifacts to a project
sa.projects.remove_artifactRemove one artifact (or item) from a project
sa.shares.add_to_artifactGive named people access to an artifact
sa.shares.add_to_projectGive named people access to a project
sa.shares.add_to_workspaceInvite editors to a workspace (returns accept links)
sa.comments.listComment threads on an artifact, newest first
sa.comments.respondReply to and/or resolve threads, one outcome each
sa.comments.get_screenshotOne message's screenshot, base64 in `image`
sa.mentions.listWhere people @mentioned you; listing marks them seen
sa.mentions.iterEvery mention, page by page
sa.signatures.listAn artifact's signature requests, newest first
sa.signatures.requestAsk named people to sign one version of an artifact
sa.signatures.getOne signature request and its signers
sa.signatures.remindEmail a signer their link again
sa.signatures.withdrawClose an open signature request