Python SDK
Release status. API 1.0.0-rc.1 is a release candidate: request and response shapes may still change before the stable release. SDK 0.2.2 is published as a Developer Preview (@morphiaone/sdk on npm and morphiaone on PyPI).
Install
The package supports Python 3.11 through 3.14. Install the published version:
The SDK is typed and depends on httpx for HTTP requests.
Keep credentials on your server
Set the key and API base URL in the server process, using your secret manager or deployment environment:
Never put the API key in a browser bundle, mobile app, or client-side configuration. Call MorphiaOne from your backend; let your frontend call your backend. See Authentication and the backend-for-frontend pattern.
Create one MorphiaOne client for a short script with with, as in the quickstart.
For a long-running service, reuse a client for the service lifespan and call
client.close() during shutdown.
Quickstart: upload and analyze
Each product request follows the same path: upload an image as an Asset, submit a job
that refers to its id, then wait for the job and read its output. This complete
server-side example expects a JPEG, PNG, or WebP path as its first command-line
argument. Replace the synthetic idempotency key with one derived from your own durable
order or request id. Save the example as quickstart.py, set the two environment
variables above, then run python quickstart.py ./face.jpg.
assets.upload accepts a path, Path, or raw bytes. It infers image/jpeg,
image/png, or image/webp from the filename or image bytes; pass media_type when
needed. The SDK rejects uploads larger than its default 10 MiB max_image_bytes
limit before sending them. Uploads have no idempotency key, so an ambiguous retry can
create an extra input Asset; use the returned expires_at to know its lifetime.
Product submissions are asynchronous. The submit returns a Job; jobs.wait polls
until it settles and returns a succeeded job. A failed job raises ProcessingError.
The job output contains typed result data and references to result Assets, not image
bytes. Fetch result bytes with assets.get_content before the Asset’s expires_at.
That method uses the configured API origin and key rather than following href as an
arbitrary URL.
Skin analysis
skin.analyze accepts the source Asset id, the concerns to analyze, and optional
visualizations. It returns exactly the concerns and visualizations requested. For a
measured concern, score is from 0 to 10 and higher means the concern is less visible.
An unavailable result has no score; do not treat it as zero. Skin analysis is cosmetic,
not a medical diagnosis, and score semantics remain draft.
The result is a SkinAnalysisOutput with analysis_version, concerns, and
visualizations. Keep analysis_version with any score you store: scores are only
comparable within the same version.
Skin score semantics are draft, and native-versus-production-WASM parity is not certified. Do not promise that these scores match the production browser SDK.
Makeup try-on
makeup.try_on takes one source Asset and an ordered list of products. The order is
the rendering layer order. Supported categories are foundation, lipstick, and
lip_liner; include each category at most once. A product has a #RRGGBB color and
optional opacity from 0 to 1 (default 1). Replace face.jpg with a path available to
your server.
Makeup rendering executes for all three categories, but pixel parity with the production browser renderer is not proven for any category. Do not promise matching output. See Makeup try-on.
Hair color try-on
Hair try-on finds the hair in one source image and recolors only that. It does not
change hairstyle or hair length. If you already segment hair yourself, upload your
grayscale mask as a second Asset (same dimensions; zero is background, nonzero is hair)
and pass hair_mask_asset_id; the server then uses it instead.
Provide a dark and light color stop. Each stop takes an #RRGGBB color and
optional opacity from 0 to 1 (default 1). Replace both file paths with files available
to your server:
Hair is a draft capability. See Hair color try-on for its limits and input requirements.
Jobs and outputs
Use client.jobs.get(job_id) to read the latest job or client.jobs.wait(job_id) to
poll until it succeeds. You can also pass the returned Job object to wait. Persist
the job id before waiting; a local timeout does not cancel work on the server. Job
states are queued, running, succeeded, and failed.
Known outputs decode as SkinAnalysisOutput, MakeupTryOnOutput, or
HairTryOnOutput. An output type added by the API that this SDK does not know becomes
UnknownJobOutput and remains available in its raw data. Check the output type
before reading product-specific fields.
wait=True on skin.analyze, makeup.try_on, or hair.try_on waits during the
submission call and returns the succeeded job. Without wait=True, each submission
returns as soon as the API accepts or replays the job.
Async client
AsyncMorphiaOne supports the same Assets, Jobs, products, and Usage methods. Every
call is a coroutine; use async with to close its HTTP client:
Usage
client.usage.get reads accepted job counts by operation and UTC day. Its optional
start_date and end_date use YYYY-MM-DD; the selected interval includes the start
and excludes the end. The report is operational data, not an invoice.
Idempotency
The SDK generates an idempotency key when a submit call omits one and reuses it across that call’s automatic retries. This only protects retries within that call. For safe retries after a process restart or lost response, pass a stable key derived from your own durable operation id and reuse it with the same input Asset and request fields. Use a new key when the logical operation or request body changes. Asset uploads are not idempotent.
The same key and same submission recover the same job. Reusing a key with different
fields or Assets raises ConflictError with IDEMPOTENCY_KEY_REUSED. See
Idempotency.
Timeouts and retries
request_timeoutdefaults to 10 seconds for one SDK request operation, including its retries.operation_timeoutdefaults to 120 seconds for a wait or a submit withwait=True; configure it on the client when work needs longer. Both are seconds, andoperation_timeoutmust be at leastrequest_timeout.- A method’s
timeoutoverrides its relevant default. On a submit withwait=True, it covers submission and polling together. Onjobs.wait, it covers the full wait. - The default
RetryPolicymakes at most four attempts. It retries transport failures, 408, 500, 502, 503, 504, retryable 429 responses, and 409IDEMPOTENCY_IN_PROGRESS. It does not blindly retryQUOTA_EXCEEDEDorINSUFFICIENT_CREDITS; a queue-limit 429 is retried only when the server sendsRetry-After. - For retried HTTP requests, the SDK honors
Retry-Afterup to 60 seconds and only when the remaining deadline can accommodate the wait; otherwise it raises the API error instead of retrying early.jobs.waitpolls usingRetry-After, capped at 60 seconds, or about one second when the server sends none. WithoutRetry-After, request retries use capped exponential backoff with jitter.
For durable integrations, persist each job id before waiting and resume with
jobs.get or jobs.wait. See Retries and backoff and
Asynchronous jobs.
Errors
Every API or client-runtime error derives from MorphiaOneError. An HTTP error is an
ApiError with status, machine-readable code, human message, details,
request_id, and retry_after in seconds when the server supplies Retry-After. Local
transport, protocol, and deadline errors have no HTTP status or request id. Branch on
code, not on message, and log request_id for support.
Invalid local arguments and uploads exceeding max_image_bytes raise ValueError
before the SDK sends a request. ProcessingError is not an HTTP error: the job request
succeeded, but its work failed. Its error carries the job error code and details.
Handle errors inside the client context around an SDK call. This fragment assumes
client and job came from your submission code:
See the error reference and status-code guide for response codes and recovery guidance.
Lists, cancel, delete, credits and webhooks
Verify a webhook delivery against the raw request body before you parse it:
The async client, AsyncMorphiaOne, has the same methods. See Jobs,
Assets, Usage and credits,
Rate limits and Webhooks for what each
call answers.