Use typesafe-sdk 0.7.1 with your blnkd API key. Tested with Noul, Choice, Score, 16-question batches, images, video, and async calls.
Install and connect
Use Python 3.10 or later.
python -m pip install "typesafe-sdk==0.7.1"Create a key in the console’s API Keys page, or follow the HTTP quickstart.
export BLNKD_URL="https://api.blnkd.dev"
export BLNKD_API_KEY="your-blnkd-api-key"Set base_url="https://api.blnkd.dev". Do not add /v1 — the SDK adds the path.
Ask typed questions
Save this as decision.py, then run python decision.py.
import os
from typesafe_sdk import Choice, Noul, RetryPolicy, Score, TypeSafeClient
with TypeSafeClient(
base_url=os.environ["BLNKD_URL"],
api_key=os.environ["BLNKD_API_KEY"],
model="blink-1.0-experimental",
timeout=280.0,
retry=RetryPolicy(max_retries=0),
) as client:
response = client.system_one(
state={
"report": "CSV export fails for three users. Dashboard and API work. Export through the API is a usable workaround.",
},
questions={
"team": Choice(
instructions="Which team should own this report?",
criteria={
"engineering": "A product feature is failing",
"billing": "A payment or invoice issue",
"clarify": "Not enough context to identify an owner",
},
),
"multiple_users": Noul(
instructions="Does the report explicitly say more than one user is affected?",
),
"impact": Score(
instructions="Rate the reported impact using the available evidence.",
criteria=[
"Cosmetic issue; no task blocked",
"One task blocked with a usable workaround",
"Core task blocked without a workaround",
"Service unavailable to all users",
],
),
},
)
print(response.choices["team"].choice)
print(response.choices["team"].probabilities)
print(response.nouls["multiple_users"].noul)
print(response.scores["impact"].score)
print(response.scores["impact"].probabilities)
print(response.usage.input_tokens, response.usage.output_tokens)The question name is the key you use to read its answer:
response.choices["team"]contains the selected label and the complete option distribution.response.nouls["multiple_users"].noulis the probability assigned to true.response.scores["impact"].scoreis a weighted mean of zero-based rubric levels. It is not a percentage.response.usagecontains the input and output token counts returned with the response.
The SDK exposes Score distribution keys as integers. The wire JSON uses strings. response.answers contains every answer if you prefer one combined mapping.
Use an async client
Use AsyncTypeSafeClient with await.
import asyncio
import os
from typesafe_sdk import AsyncTypeSafeClient, Choice, RetryPolicy
async def main():
async with AsyncTypeSafeClient(
base_url=os.environ["BLNKD_URL"],
api_key=os.environ["BLNKD_API_KEY"],
model="blink-1.0-experimental",
timeout=280.0,
retry=RetryPolicy(max_retries=0),
) as client:
response = await client.system_one(
state={"ticket": "My delivery is missing."},
questions={
"team": Choice(
instructions="Which team should handle this ticket?",
criteria={"shipping": "Delivery issues", "billing": "Payment issues"},
),
},
)
print(response.choices["team"].choice)
asyncio.run(main())Run one request at a time per account. Overlapping requests return 429.
Send an image
Pass an image through extra_body. This example uses package.jpg.
import base64
import os
from pathlib import Path
from typesafe_sdk import Choice, RetryPolicy, TypeSafeClient
# Use a local JPEG. Set the MIME type to match your file for PNG or WebP.
image = base64.b64encode(Path("package.jpg").read_bytes()).decode("ascii")
with TypeSafeClient(
base_url=os.environ["BLNKD_URL"],
api_key=os.environ["BLNKD_API_KEY"],
model="blink-1.0-experimental",
timeout=280.0,
retry=RetryPolicy(max_retries=0),
) as client:
response = client.system_one(
state="Inspect visible packaging only. Do not infer damage to the contents.",
questions={
"condition": Choice(
instructions="What condition is directly visible?",
criteria={
"damaged": "Visible crushing, tearing, or puncture",
"intact": "Packaging appears intact",
"unclear": "Not enough visual evidence",
},
),
},
extra_body={"images": [f"data:image/jpeg;base64,{image}"]},
)
print(response.choices["condition"].choice)
print(response.choices["condition"].probabilities)Send one PNG, JPEG, or WebP image. Use the correct MIME type and keep the entire JSON request below the API’s 16 MiB body limit. See Images for task design and Limits for request limits.
Send a video
Use extra_body for video too. Replace the image field in the example above:
video = base64.b64encode(Path("clip.mp4").read_bytes()).decode("ascii")
response = client.system_one(
state="Inspect the attached clip.",
questions={
"outdoors": Noul(instructions="Is the scene outdoors?"),
},
extra_body={"videos": [f"data:video/mp4;base64,{video}"]},
)
print(response.nouls["outdoors"].noul)Run this inside the client context. Import Noul from typesafe_sdk. Send one MP4 up to 10 MiB and 30 seconds. See Video inputs.
Handle errors
Catch TypeSafeAPIError and read error.status:
401— check the API key.402— account quota reached.422— check the input and model limits.429— wait for the active request to finish.502— the model request failed.
The examples disable automatic retries. A retry can count as another request. A client timeout does not mean the server stopped processing.
Supported calls
Use system_one() with model="blink-1.0-experimental". Responses are complete JSON objects. Streaming and model discovery are not supported.
See the SDK reference for client options.