meta_title: Programmatic Email Alias Creation with Python: Guide meta_description: Create email aliases from Python with the Emcognito API: bearer auth, POST /v1/aliases, pagination, and how to handle the 429s that differ.
You can implement programmatic email alias creation with python using two HTTP endpoints on the Emcognito v1 API without importing third-party provider SDKs. Sending an authenticated JSON payload to POST /v1/aliases provisions a durable forwarding address immediately, returning a structured object containing the assigned routing address and metadata.
If you have already automated test workflows, built SaaS provisioning scripts, or partitioned your signups per vendor to isolate data exposure, you do not need an explanation of why forwarding aliases matter. You need the exact request contracts, payload shapes, pagination cursors, and failure modes to create aliases cleanly in your codebase. This guide details how to build against the API, parse the actual response schema, and handle the distinct rate-limit thresholds enforced across accounts.
What the Emcognito v1 API Gives You in Python
The Emcognito v1 developer surface is intentionally minimal. It exposes two endpoints rooted at https://api.emcognito.com/v1:
GET /v1/aliases: Lists account aliases with cursor pagination.POST /v1/aliases: Creates a new forwarding alias.
Note that the path is strictly https://api.emcognito.com/v1; requests routed to /api/v1 will fail. Authentication uses standard bearer tokens passed via the Authorization header:
Authorization: Bearer emk_your_key_here
Your API key consists of the literal prefix emk_ followed by 43 URL-safe characters. There is no environment split: keys do not use test or live prefixes such as emk_test_ or emk_live_. A single key accesses your production account context.
When creating an alias via POST /v1/aliases, the endpoint accepts an optional JSON object containing metadata tags: Source: Emcognito source.
label(string): A short, human-readable name for the alias.note(string): Extended context or purpose documentation.source(string): The origin system, host, or script provisioning the record.category(string): Organizational tag for grouping related records.single_use(boolean): Flag indicating whether the alias is intended for single verification interactions.expires_at(integer): Optional Unix timestamp marking when the alias should stop forwarding.
v1 of the Emcognito API creates and lists aliases (GET and POST /v1/aliases). Suspending, resuming and deleting an alias are one-click dashboard actions today; there is no PATCH or DELETE endpoint yet. If your workflow requires creating and auditing aliases from code, the API fits that scope directly. If your pipeline demands automated programmatic destruction or automated state toggling via script, those operations must be managed manually through the web dashboard.
Before You Write Code: Keys, Plans and the Daily Creation Cap
Access to the developer API requires an active paid tier, detailed on the Emcognito pricing page. Accounts operate under plan-specific creation ceilings:
- Emcognito Plus: Paid tier providing 2,500 forwards per month, compose new mail from any alias, and developer API creation limited to 50 aliases per day.
- Emcognito Pro: Higher-tier plan offering 15,000 forwards per month, compose from any alias with higher daily sending allowances, and developer API creation scaled to 200 aliases per day. Annual Pro includes three months free and is the term customers actually buy.
The total number of aliases stored on your account is unlimited across all plans, including the Free tier. The quota restricts new alias creation velocity per day. Manual dashboard creation is capped at 200 per day across tiers. Over the API, your rate ceiling is determined by your plan: 50 creations daily on Plus, or 200 creations daily on Pro. Source: Emcognito source.
This daily creation counter resets promptly at 00:00 UTC. If your scripts run automated batch provisioning near your local midnight, ensure your cron jobs or worker queues calculate their execution buffers relative to UTC midnight. Both Plus and Pro come with one 7-day free trial per account.
Separate from the daily creation quota, the API enforces a global burst limit of 60 requests per minute per key. While alias creation is subject to both the burst limit and the daily plan cap, reading aliases via GET /v1/aliases consumes only your per-minute burst budget.
Your First Alias: A Minimal Python Request
You can execute requests using standard HTTP libraries like Requests or HTTPX. The snippet below demonstrates standard creation using requests:
import datetime import requestsAPI_URL = "https://api.emcognito.com/v1/aliases" API_KEY = "emk_placeholder_your_43_character_token_value_here"
headers = { "Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json" }
payload = { "label": "cloud-monitoring-alert", "note": "Production alert ingest for server cluster B", "source": "terraform-provisioning", "category": "infrastructure" }
response = requests.post(API_URL, json=payload, headers=headers, timeout=10)
if response.status_code == 200: data = response.json() alias = data["alias"] print(f"Created alias: {alias['address']}") print(f"Status: {alias['status']}")
# created_at returns an integer epoch created_dt = datetime.datetime.fromtimestamp(alias["created_at"], tz=datetime.timezone.utc) print(f"Timestamp: {created_dt.isoformat()}")
else: print(f"Failed with status code {response.status_code}: {response.text}")
Review the exact schema contracts returned by this call:
- HTTP Status 200: The endpoint returns
200 OKupon creation, not201 Created. Do not write assertion logic or dispatch branches expecting a 201 response. - Top-Level Structure: The response does not wrap payloads inside generic status metadata like
{"status": "success", "data": {...}}, nor does it return flat keys. Everything resides under the root"alias"key. - Response Keys: The
aliasdictionary contains exactly:id,address,status,created_at,forward_count,label,note,source,category,single_use, andexpires_at. - Integer Timestamps: The field
created_atis provided as an integer Unix epoch. As described in the official Python datetime documentation, convert this usingdatetime.datetime.fromtimestamp(alias["created_at"], tz=datetime.timezone.utc)to ensure proper timezone handling. - Empty Strings vs Nulls: Unassigned string attributes return as empty strings (
""), rather than JSONnull. If your code verifies field contents, check falsiness (if not alias["note"]:) rather than matching againstNone.
Labelling Aliases Per Site So a Leak Is Traceable
Creating aliases through automated code is primarily useful when every discrete client, test run, or third-party service receives its own isolated address. When an address is leaked, sold, or hit with unexpected traffic, the metadata attached to that alias reveals the source of the exposure.
To audit exposures systematically without running free-form text search, standardize your string labels at creation time:
def generate_payload(vendor_domain: str, environment: str = "prod") -> dict:
today_str = datetime.date.today().isoformat()
return {
"label": f"{vendor_domain}-{today_str}",
"source": f"backend-automation-{environment}",
"category": "vendor-signup",
"note": f"Automated registration for {vendor_domain} integration test",
"single_use": False
}
Structuring the label with a deterministic prefix (such as the vendor's domain name) and a creation date guarantees consistent sorting across the list endpoint. If unwanted mail arrives at that alias, check the incoming recipient address against your records. This immediately confirms which vendor system experienced the breach or distributed your address.
When testing one-off account verification flows that only require confirming a token, pass "single_use": True alongside an explicit expires_at epoch. While Emcognito functions as a durable forwarding layer rather than a temporary burner mailbox, these attributes let your scripts automatically restrict forwarding persistence for short-lived workflows.
Listing and Paginating Aliases Without Missing Records
Auditing existing addresses or synchronizing internal databases requires calling GET /v1/aliases. Emcognito uses cursor-based pagination controlled via next_cursor and last_key.
Do not rely on a fixed response length or guess an offset limit. Iterate through the records until the API returns an empty cursor signal:
import requests
import time
def fetch_all_aliases(api_key: str):
base_url = "https://api.emcognito.com/v1/aliases"
headers = {"Authorization": f"Bearer {api_key}"}
all_records = []
cursor = None
while True:
params = {}
if cursor:
params["cursor"] = cursor
response = requests.get(base_url, headers=headers, params=params, timeout=10)
if response.status_code != 200:
raise RuntimeError(f"Request failed: {response.status_code} {response.text}")
payload = response.json()
aliases = payload.get("aliases", [])
all_records.extend(aliases)
# Advance via cursor pagination
cursor = payload.get("next_cursor")
if not cursor:
break
# Respect per-key burst pacing: 60 calls/minute maximum
time.sleep(1.05)
return all_records
When reading large directories, keep local pacing under the burst rate. Calling GET /v1/aliases does not exhaust your daily creation cap (which meters only new address provisioning), but it is fully subject to the 60 requests per minute burst limit.
Do not attempt to read rate-limit headers to calibrate your pacing. Aside from Retry-After, Emcognito sends no rate-limit headers. If you need local persistence, store the returned id and address fields in a lightweight SQLite table, querying only the delta during your scheduled runs.
Handling the Two 429s: They Are Not the Same
One critical implementation detail of the Emcognito API is that client applications can encounter two fundamentally different HTTP 429 Too Many Requests errors. They originate from separate layers of the platform architecture and must be parsed differently.
As outlined in RFC 6585 (Additional HTTP Status Codes), an HTTP 429 indicates rate-limiting conditions. However, the presence and behavior of headers depends directly on which boundary was triggered:
- The Gateway Burst Limit (60 req/min): This is managed by the rate limiter. When triggered (for example, on the 61st call within a 60-second window), the response delivers an HTTP 429 with an explicit
Retry-Afterheader populated in integer seconds. Live measurements taken on 2026-09-20 confirmed that exceeding the threshold returnsretry-after: 60, indicating the full window reset requirement. - The Daily Creation Cap (50 or 200 aliases/day): This is enforced within application code based on your subscription tier (Emcognito Plus or Pro). When your account reaches its daily creation allowance, the API returns an HTTP 429 containing no
Retry-Afterheader. This cap remains active until the platform resets at 00:00 UTC.
POST /v1/aliases returns HTTP 200 with the new alias under "alias". The burst limit is 60 requests a minute per key; its 429 carries a Retry-After header giving the seconds until the window resets. The daily alias-creation cap (50 on Plus, 200 on Pro) resets at 00:00 UTC and its 429 carries no Retry-After. Emcognito sends no X-RateLimit-* headers.
Additionally, developer API infrastructure may return an HTTP 503 under heavy load, which carries a static Retry-After: 30 header. Aside from the burst 429 and rate-limit 503, Retry-After is absent from developer API responses.
Here is how to structure a robust Python handler using standard timing tools from the Python time module to navigate both conditions safely:
import datetime
import time
import requests
def seconds_until_utc_midnight() -> int:
now = datetime.datetime.now(datetime.timezone.utc)
tomorrow = now.date() + datetime.timedelta(days=1)
midnight = datetime.datetime(tomorrow.year, tomorrow.month, tomorrow.day, 0, 0, 0, tzinfo=datetime.timezone.utc)
return int((midnight - now).total_seconds()) + 5 # Include a 5-second buffer
def safe_create_alias(payload: dict, api_key: str) -> dict:
url = "https://api.emcognito.com/v1/aliases"
headers = {
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json"
}
while True:
response = requests.post(url, json=payload, headers=headers, timeout=10)
if response.status_code == 200:
return response.json()["alias"]
if response.status_code == 429:
retry_header = response.headers.get("Retry-After")
if retry_header:
# Per-key burst limit encountered
wait_time = int(retry_header)
print(f"Burst limit reached. Waiting {wait_time}s as directed by Retry-After...")
time.sleep(wait_time)
continue
else:
# Daily creation limit reached (no Retry-After header provided)
wait_time = seconds_until_utc_midnight()
print(f"Daily creation cap reached. Window resets at 00:00 UTC ({wait_time}s remaining).")
# In production, route to a dead-letter queue or sleep until tomorrow
raise ResourceWarning("Daily creation quota reached for current tier.")
if response.status_code == 503 and "Retry-After" in response.headers:
wait_time = int(response.headers["Retry-After"])
print(f"Service busy. Retrying in {wait_time}s...")
time.sleep(wait_time)
continue
response.raise_for_status()
Where Python Automation Stops: Dashboard-Only Lifecycle Actions
Programmatic pipelines must be architected around the boundaries of what the API implements. Today, v1 covers address minting and enumeration. Suspending, resuming, and deleting addresses are strictly manual, authenticated dashboard interactions.
If your script monitors incoming email patterns or processes third-party data breaches, it cannot dispatch an automated HTTP request to alter alias statuses. When an address receives unverified or unwanted traffic, you isolate it by clicking Pause alias in the web dashboard. Pausing an alias immediately halts all inbound forwarding without destroying the routing history. Deletion requires an explicit confirmation modal in the UI.
If your operational model demands an automated webhook or scriptable endpoint to terminate aliases instantly upon an alert trigger, review that architecture beforehand. Emcognito is closed source and runs only as a hosted service; if open source or self-hosting is a requirement, addy.io is the better fit.
A Repeatable Pattern: Signup-Time Alias Creation from Python
A common design pattern is incorporating alias provisioning into integration tests or vendor onboarding tools. The client script requests an alias, associates it with the vendor's record in local storage, and then uses that address when populating registration forms.
import datetime
import sqlite3
import requests
class AliasClient:
def init(self, api_key: str, db_path: str = "aliases.db"):
self.api_key = api_key
self.base_url = "https://api.emcognito.com/v1"
self.db_path = db_path
self._init_storage()
def _init_storage(self):
with sqlite3.connect(self.db_path) as conn:
conn.execute("""
CREATE TABLE IF NOT EXISTS assigned_aliases (
id TEXT PRIMARY KEY,
address TEXT NOT NULL,
vendor TEXT NOT NULL,
created_at INTEGER NOT NULL
)
""")
def provision_for_vendor(self, vendor: str) -> str:
url = f"{self.base_url}/aliases"
headers = {"Authorization": f"Bearer {self.api_key}"}
payload = {
"label": f"vendor-{vendor}",
"source": "automated-client",
"category": "integrations"
}
resp = requests.post(url, json=payload, headers=headers, timeout=10)
if resp.status_code != 200:
raise RuntimeError(f"API Error {resp.status_code}: {resp.text}")
record = resp.json()["alias"]
with sqlite3.connect(self.db_path) as conn:
conn.execute(
"INSERT INTO assigned_aliases (id, address, vendor, created_at) VALUES (?, ?, ?, ?)",
(record["id"], record["address"], vendor, record["created_at"])
)
return record["address"]
For manual, day-to-day web browsing, programmatic scripts are unnecessary. Emcognito provides a Chrome browser extension that creates an alias inline on any signup form. Keep your Python scripts focused on batch registrations, scheduled tests, and server-side automation.
Remember that provisioning an alias via the API does not alter how outbound messages are sent. Composing brand-new emails from an existing alias is available on paid plans. Outbound messages carry their own daily sending limits (20 per day on Plus, 100 per day on Pro, ramping up over the first 7 days) and every compose send counts against the same monthly quota as forwards.
When the Emcognito API Is the Wrong Choice
Every engineering team must evaluate operational constraints objectively. Emcognito is not the right technical solution for every forwarding architecture:
- Custom domain requirements : Emcognito aliases use the shared emcognito.com domain. Custom subdomain support is planned, but custom domains are not available today. Workflows requiring third-party DNS configuration or external mail routing should evaluate alternative platforms.
- You require programmatic address termination: As noted, v1 does not expose endpoints to pause or delete records. If your compliance policies require scheduled, automated address purging via script, the manual dashboard requirement may block your workflow.
- You require an open-source or self-hosted stack: Emcognito is closed source and runs only as a hosted service; if open source or self-hosting is a requirement, addy.io is the better fit.
- You require high-velocity address creation: Emcognito is engineered for long-term identity partitioning, not throwaway disposable addresses. If your application needs to generate thousands of fresh addresses every hour, the hard plan caps (50/day on Plus, 200/day on Pro) will not fit your scale.
Frequently Asked Questions
Does the Emcognito API return 201 when it creates an alias?
No. POST /v1/aliases returns an HTTP 200 OK response upon successful creation. Do not configure your HTTP client, status checks, or retry wrappers to depend on an HTTP 201 Created code.
How do I tell a burst-limit 429 from a daily-cap 429 in Python?
Inspect the HTTP response headers. A 429 triggered by the 60 request-per-minute burst limit includes a Retry-After header containing the integer seconds until the rate-limiter resets. A 429 triggered by exceeding your daily creation quota (50 on Plus, 200 on Pro) contains no Retry-After header; this quota resets independently at 00:00 UTC.
Can I suspend or delete an alias through the API?
No. v1 of the Emcognito API creates and lists aliases (GET and POST /v1/aliases). Suspending, resuming and deleting an alias are one-click dashboard actions today; there is no PATCH or DELETE endpoint yet.
What is the daily alias creation limit on Plus and Pro?
On Emcognito Plus, accounts can generate up to 50 aliases per day via the developer API. On Emcognito Pro, accounts can generate up to 200 aliases per day. Both counters reset at 00:00 UTC. Total stored aliases remain unlimited on all plans.
Is created_at an ISO string or an epoch integer?
The created_at attribute is delivered as an integer Unix epoch. You can convert it into a timezone-aware object in Python using datetime.datetime.fromtimestamp(alias["created_at"], tz=datetime.timezone.utc).
Next Step
To start provisioning aliases from your codebase, review the endpoint specifications and payload schemas on the developer alias API, included with both Plus ($20/year) and Pro ($36/year).