Connectors
Import datasets from V7 (Darwin) and Roboflow into Pictograph. Annotations are converted to canonical Pictograph JSON automatically.
Import remote datasets in two steps: validate the source API key → kick off the import. The import runs as an async job; the SDK polls until terminal status by default.
Every example below shows the Python SDK call and the equivalent raw REST
request. The REST examples authenticate with an X-API-Key header; set
PICTOGRAPH_API_KEY in your shell to copy-and-run them. The source
provider’s key (V7 token / Roboflow key) is a separate value sent in the
request body — it is used only for the call that needs it and never
persisted.
from pictograph import Client
client = Client() # reads PICTOGRAPH_API_KEY
Supported providers
provider | Source | Notes |
|---|---|---|
v7 | V7 Darwin | Polygon paths, bboxes, polylines, keypoints, tags |
roboflow | Roboflow | COCO export → Pictograph JSON |
validate
Verify the source API key and list available remote datasets. No quota consumed; the source API key is sent only on this call.
| Arg | Type | Default | Notes |
|---|---|---|---|
provider | ConnectorProvider | required | "v7" / "roboflow" |
api_key | str | required | The source provider’s API key |
result = client.connectors.validate(
provider="v7",
api_key="v7_api_token_…",
)
if result.valid:
for ds in result.datasets:
print(ds.id, ds.name, ds.image_count)
else:
print("invalid:", result.error)
curl -s -X POST "https://api.pictograph.io/api/v1/developer/connectors/validate" \
-H "X-API-Key: $PICTOGRAPH_API_KEY" \
-H "Content-Type: application/json" \
-d '{"provider": "v7", "api_key": "v7_api_token_…"}'
Returns ValidationResult — inspect .valid first; .datasets is
populated only on success.
check_limits
Pre-flight tier-cap check before kicking off an import.
| Arg | Type | Default | Notes |
|---|---|---|---|
total_images | int | required | Images about to be imported |
estimated_size_bytes | int | required | Estimated total import size, in bytes |
check = client.connectors.check_limits(
total_images=12500,
estimated_size_bytes=4_000_000_000, # 4 GB
)
if not check.allowed:
print("blocked by:", check.exceeded) # "images" / "storage" / "both"
curl -s -X POST "https://api.pictograph.io/api/v1/developer/connectors/check-limits" \
-H "X-API-Key: $PICTOGRAPH_API_KEY" \
-H "Content-Type: application/json" \
-d '{"total_images": 12500, "estimated_size_bytes": 4000000000}'
Returns LimitCheckResult.
import_
Kick off the import. The trailing underscore avoids shadowing the Python
import keyword. The REST endpoint returns an import_id immediately;
the SDK then polls until terminal status when wait=True.
| Arg | Type | Default | Notes |
|---|---|---|---|
provider | ConnectorProvider | required | "v7" / "roboflow" |
api_key | str | required | Sent only to fetch source data |
datasets | Sequence[RemoteDataset | dict] | required | RemoteDataset instances or raw dicts |
wait | bool | True | Poll until terminal |
poll_interval | float | 3.0 | seconds |
timeout | float | 3600.0 | Max poll seconds (V7 large exports take 30+ min) |
job = client.connectors.import_(
provider="v7",
api_key="v7_api_token_…",
datasets=[
{"id": "ds_abc", "name": "Road signs", "slug": "road-signs"},
# OR pass RemoteDataset instances from validate():
# *result.datasets[:2],
],
wait=True,
poll_interval=3.0,
timeout=3600.0, # 1h default
)
print(job.import_id, job.status)
for ds in job.datasets:
print(ds.name, ds.imported, "/", job.total_images)
curl -s -X POST "https://api.pictograph.io/api/v1/developer/connectors/import/start" \
-H "X-API-Key: $PICTOGRAPH_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"provider": "v7",
"api_key": "v7_api_token_…",
"datasets": [
{"id": "ds_abc", "name": "Road signs", "slug": "road-signs"}
]
}'
Returns ImportJob. The raw REST response carries import_id plus the
created project list; poll the status endpoint below for progress.
get_import
Fetch the current state of an import job.
job = client.connectors.get_import(import_id)
print(job.status, job.progress, job.imported_images)
curl -s "https://api.pictograph.io/api/v1/developer/connectors/import/status/{import_id}" \
-H "X-API-Key: $PICTOGRAPH_API_KEY"
Returns ImportJob. Status transitions processing → completed (or
error / cancelled).
wait_for_import
Poll an import until it reaches a terminal status (SDK convenience over
get_import; uses the same status endpoint).
job = client.connectors.wait_for_import(import_id, timeout=600.0)
# Poll the status endpoint until status is completed / error / cancelled.
curl -s "https://api.pictograph.io/api/v1/developer/connectors/import/status/{import_id}" \
-H "X-API-Key: $PICTOGRAPH_API_KEY"
cancel_import
Soft-cancel an in-flight import. Already-imported images are kept; the
worker stops downloading new ones and the status transitions to
cancelled.
job = client.connectors.cancel_import(import_id)
curl -s -X POST "https://api.pictograph.io/api/v1/developer/connectors/import/cancel/{import_id}" \
-H "X-API-Key: $PICTOGRAPH_API_KEY"
Returns ImportJob.
CLI equivalents:
pictograph connectors validate v7 --key v7_api_token_…
pictograph connectors check-limits --images 12500 --bytes 4000000000
pictograph connectors import v7 --key v7_api_token_… --dataset ds_abc
pictograph connectors status <import_id>
pictograph connectors cancel <import_id>
Annotation conversion
| V7 / COCO | Pictograph |
|---|---|
V7 polygon.paths | polygon.paths (passthrough) |
V7 bounding_box (no polygon) | bounding_box |
V7 line.path | polyline.path |
V7 keypoint | keypoint |
V7 tag / ellipse / mask | skipped (no Pictograph equivalent) |
COCO segmentation (flat array) | polygon.paths (paired into points) |
COCO bbox (no segmentation) | bounding_box |
COCO keypoints triplets | keypoint (skips v=0) |
Tier caps
Imports are charged against your storage + image-count tier caps. See Credits and your plan in the web app.
Common errors
| Status | Exception | Cause |
|---|---|---|
| 401 | AuthError | Source provider API key rejected |
| 402 | PaymentRequiredError | Tier cap exceeded |
| 403 | ForbiddenError | import / cancel require member+ role |
| 404 | NotFoundError | import_id missing |
| 408 | PollTimeoutError | wait=True exceeded timeout (job keeps running) |
| 422 | ValidationError | Invalid provider, empty datasets list |