SigninID Sandbox is shutting down on July 31, 2026. New sign-ups are disabled; existing accounts remain accessible until then.

Python SDK

Official Python client for SigninID. Supports both sync and async operations.

Installation

pip install signinid

Requires Python 3.10 or later.

Basic Usage

from signinid import SigninID

# Set SIGNINID_SECRET_KEY environment variable
client = SigninID()

# Wait for a new verification email to arrive
email = client.inbox.wait_for_new(to="user@test.com")

# Extract the OTP
if email:
    print(f"Verification code: {email.detected_otp}")

Async Support

import asyncio
from signinid import AsyncSigninID

async def main():
    async with AsyncSigninID(secret_key="sk_live_...") as client:
        email = await client.inbox.wait_for_new(to="user@test.com")
        if email:
            print(f"OTP: {email.detected_otp}")

asyncio.run(main())

Configuration

The client reads from SIGNINID_SECRET_KEY environment variable by default. You can also pass the secret key directly:

# Using environment variable (recommended)
client = SigninID()

# Or pass secret key directly
client = SigninID(secret_key="sk_live_...")

Client Options

OptionTypeDefaultDescription
secret_keystr | Noneenv varSecret key (falls back to SIGNINID_SECRET_KEY)
timeoutint30000Request timeout in milliseconds

Inbox Methods

client.inbox.wait_for_new(**params)

Wait for a new email to arrive. Polls the inbox until a new email arrives or timeout is reached. This is the recommended method for testing signup/login flows.

Example

email = client.inbox.wait_for_new(to="user@test.com")
if email:
    print(f"OTP: {email.detected_otp}")

Parameters

ParameterTypeDefaultDescription
tostr | None-Filter by recipient (partial match)
timeoutint30Maximum wait time in seconds

Response Type: InboxEmail | None

Returns None if timeout reached.

FieldTypeDescription
email_idstrUnique identifier
detected_otpstr | NoneAuto-extracted verification code
html_bodystr | NoneHTML content
text_bodystr | NonePlain text content
from_addressstrSender email address
from_namestr | NoneSender display name
to_addressestuple[str, ...]Recipient addresses
cc_addressestuple[str, ...] | NoneCC addresses
subjectstr | NoneEmail subject
received_atdatetimeWhen email was received
message_idstr | NoneSMTP Message-ID header
has_attachmentsboolHas attachments
attachment_countintNumber of attachments
spam_scorefloat | NoneSpam score
spam_verdictPASS | FAIL | GRAY | NoneSpam classification
spam_rulesSpamRules | NoneDetailed spam rules
virus_verdictSecurityVerdictVirus scan result
spf_verdictSecurityVerdictSPF authentication
dkim_verdictSecurityVerdictDKIM verification
dmarc_verdictSecurityVerdictDMARC compliance

client.inbox.latest(**params)

Get the most recent inbox email. Returns immediately with the latest email or None.

Example

email = client.inbox.latest(to="user@test.com")
if email:
    print(f"OTP: {email.detected_otp}")

Parameters

ParameterTypeDefaultDescription
tostr | None-Filter by recipient (partial match)
afterdatetime | str | None-Only return emails received after this timestamp

Response Type: InboxEmail | None

FieldTypeDescription
email_idstrUnique identifier
detected_otpstr | NoneAuto-extracted verification code
html_bodystr | NoneHTML content
text_bodystr | NonePlain text content
from_addressstrSender email address
from_namestr | NoneSender display name
to_addressestuple[str, ...]Recipient addresses
cc_addressestuple[str, ...] | NoneCC addresses
subjectstr | NoneEmail subject
received_atdatetimeWhen email was received
message_idstr | NoneSMTP Message-ID header
has_attachmentsboolHas attachments
attachment_countintNumber of attachments
spam_scorefloat | NoneSpam score
spam_verdictPASS | FAIL | GRAY | NoneSpam classification
spam_rulesSpamRules | NoneDetailed spam rules
virus_verdictSecurityVerdictVirus scan result
spf_verdictSecurityVerdictSPF authentication
dkim_verdictSecurityVerdictDKIM verification
dmarc_verdictSecurityVerdictDMARC compliance

client.inbox.get(email_id)

Get a single inbox email by ID.

Example

email = client.inbox.get("550e8400-e29b-41d4-a716-446655440000")
print(f"Subject: {email.subject}")

Parameters

ParameterTypeDescription
email_idstrThe email ID (UUID format)

Response Type: InboxEmail

Raises NotFoundError if not found.

FieldTypeDescription
email_idstrUnique identifier
detected_otpstr | NoneAuto-extracted verification code
html_bodystr | NoneHTML content
text_bodystr | NonePlain text content
from_addressstrSender email address
from_namestr | NoneSender display name
to_addressestuple[str, ...]Recipient addresses
cc_addressestuple[str, ...] | NoneCC addresses
subjectstr | NoneEmail subject
received_atdatetimeWhen email was received
message_idstr | NoneSMTP Message-ID header
has_attachmentsboolHas attachments
attachment_countintNumber of attachments
spam_scorefloat | NoneSpam score
spam_verdictPASS | FAIL | GRAY | NoneSpam classification
spam_rulesSpamRules | NoneDetailed spam rules
virus_verdictSecurityVerdictVirus scan result
spf_verdictSecurityVerdictSPF authentication
dkim_verdictSecurityVerdictDKIM verification
dmarc_verdictSecurityVerdictDMARC compliance

client.inbox.list(**params)

List inbox email IDs with pagination. Returns only IDs - use get() to fetch full details.

Example

response = client.inbox.list(per_page=20)
for email_id in response:
    email = client.inbox.get(email_id)
    print(email.subject)

Parameters

ParameterTypeDefaultDescription
pageint | None1Page number (1, 2, 3...)
per_pageint | None10Results per page (1-100)
from_str | None-Filter by sender (partial match)
tostr | None-Filter by recipient (partial match)
subjectstr | None-Filter by subject (partial match)
beforedatetime | str | None-Emails before this date
afterdatetime | str | None-Emails after this date

Note: Use from_ (with underscore) since from is a Python reserved word.

Response Type: ListIdsResponse

Iterable collection of email IDs with pagination metadata.

FieldTypeDescription
datatuple[str, ...]Tuple of email IDs
pagination.pageintCurrent page number
pagination.per_pageintItems per page
pagination.returnedintActual count returned
pagination.has_moreboolMore results available

Sent Methods

client.sent.latest(**params)

Get the most recent sent email. Returns immediately with the latest email or None.

Example

email = client.sent.latest(to="user@example.com")
if email:
    print(f"Spam score: {email.spam_score}")

Parameters

ParameterTypeDefaultDescription
tostr | None-Filter by recipient (partial match)
afterdatetime | str | None-Only return emails sent after this timestamp

Response Type: SentEmail | None

FieldTypeDescription
email_idstrUnique identifier
detected_otpstr | NoneAuto-extracted verification code
html_bodystr | NoneHTML content
text_bodystr | NonePlain text content
from_addressstrSender email address
from_namestr | NoneSender display name
to_addressestuple[str, ...]Recipient addresses
cc_addressestuple[str, ...] | NoneCC addresses
bcc_addressestuple[str, ...] | NoneBCC addresses
subjectstr | NoneEmail subject
sent_atdatetimeWhen email was sent
message_idstr | NoneSMTP Message-ID header
has_attachmentsboolHas attachments
attachment_countintNumber of attachments
spam_scorefloat | NoneSpam score
spam_verdictPASS | FAIL | GRAY | NoneSpam classification

client.sent.get(email_id)

Get a single sent email by ID.

Example

email = client.sent.get("660f9500-f39c-52e5-b827-557766551111")
print(f"Subject: {email.subject}")

Parameters

ParameterTypeDescription
email_idstrThe email ID (UUID format)

Response Type: SentEmail

Raises NotFoundError if not found.

FieldTypeDescription
email_idstrUnique identifier
detected_otpstr | NoneAuto-extracted verification code
html_bodystr | NoneHTML content
text_bodystr | NonePlain text content
from_addressstrSender email address
from_namestr | NoneSender display name
to_addressestuple[str, ...]Recipient addresses
cc_addressestuple[str, ...] | NoneCC addresses
bcc_addressestuple[str, ...] | NoneBCC addresses
subjectstr | NoneEmail subject
sent_atdatetimeWhen email was sent
message_idstr | NoneSMTP Message-ID header
has_attachmentsboolHas attachments
attachment_countintNumber of attachments
spam_scorefloat | NoneSpam score
spam_verdictPASS | FAIL | GRAY | NoneSpam classification

client.sent.list(**params)

List sent email IDs with pagination. Returns only IDs - use get() to fetch full details.

Example

response = client.sent.list(per_page=20)
for email_id in response:
    email = client.sent.get(email_id)
    print(email.subject)

Parameters

ParameterTypeDefaultDescription
pageint | None1Page number (1, 2, 3...)
per_pageint | None10Results per page (1-100)
from_str | None-Filter by sender (partial match)
tostr | None-Filter by recipient (partial match)
subjectstr | None-Filter by subject (partial match)
beforedatetime | str | None-Emails before this date
afterdatetime | str | None-Emails after this date

Response Type: ListIdsResponse

FieldTypeDescription
datatuple[str, ...]Tuple of email IDs
pagination.pageintCurrent page number
pagination.per_pageintItems per page
pagination.returnedintActual count returned
pagination.has_moreboolMore results available

Async Methods

AsyncSigninID provides the same API with await:

import asyncio
from signinid import AsyncSigninID

async def main():
    async with AsyncSigninID() as client:
        # Wait for new email
        email = await client.inbox.wait_for_new(to="user@test.com")

        # Get latest
        email = await client.inbox.latest(to="user@test.com")

        # List and fetch
        inbox_ids = await client.inbox.list(to="user@test.com")
        for email_id in inbox_ids:
            full_email = await client.inbox.get(email_id)
            print(full_email.detected_otp)

asyncio.run(main())

Error Handling

The SDK raises typed exceptions for different failure scenarios:

ExceptionWhen Raised
AuthenticationErrorInvalid or missing API key
NotFoundErrorEmail not found (404)
ValidationErrorInvalid request parameters
RateLimitErrorToo many requests
NetworkErrorConnection failed
TimeoutErrorRequest timed out
from signinid import (
    SigninID,
    AuthenticationError,
    NotFoundError,
    ValidationError,
    RateLimitError,
    NetworkError,
    TimeoutError,
)

try:
    email = client.inbox.get("invalid-id")
except NotFoundError:
    print("Email not found")
except AuthenticationError:
    print("Invalid API key")
except ValidationError as e:
    print(f"Invalid parameters: {e.details}")
except RateLimitError as e:
    print(f"Rate limited. Retry after: {e.retry_after}s")
except NetworkError:
    print("Network error")
except TimeoutError:
    print("Request timed out")