Forminit Python SDK Reference
The official Python SDK for Forminit form backend service.
Installation
Section titled “Installation”pip install forminit
Requirements: Python 3.8+
Quick Start
Section titled “Quick Start”Synchronous
Section titled “Synchronous”from forminit import ForminitClient
client = ForminitClient(api_key="fi_your_secret_api_key")
response = client.submit("YOUR_FORM_ID", {
"blocks": [
{
"type": "sender",
"properties": {
"email": "john@example.com",
"firstName": "John",
"lastName": "Doe",
},
},
{
"type": "text",
"name": "message",
"value": "Hello world",
},
],
})
if response.get("error"):
print("Error:", response["error"]["message"])
else:
print("Submission ID:", response["data"]["hashId"])
client.close()
Asynchronous
Section titled “Asynchronous”from forminit import AsyncForminitClient
async with AsyncForminitClient(api_key="fi_your_secret_api_key") as client:
response = await client.submit("YOUR_FORM_ID", {
"blocks": [
{
"type": "sender",
"properties": {
"email": "john@example.com",
"firstName": "John",
},
},
{
"type": "text",
"name": "message",
"value": "Hello world",
},
],
})
Configuration
Section titled “Configuration”| Option | Type | Description |
|---|---|---|
api_key | str | Server-side API key for authentication |
proxy_url | str | Client-side proxy URL to protect API key |
base_url | str | Custom base URL (default: https://forminit.com) |
# Server-side with API key
client = ForminitClient(api_key="fi_your_secret_api_key")
# With proxy URL
client = ForminitClient(proxy_url="http://localhost:8000/api/forminit")
Class: ForminitClient
Section titled “Class: ForminitClient”Synchronous client using httpx. Supports context manager protocol.
Constructor
Section titled “Constructor”ForminitClient(api_key=None, proxy_url=None, base_url="https://forminit.com")
Methods
Section titled “Methods”submit()
Section titled “submit()”client.submit(form_id, data, tracking=None)
Submits form data to Forminit.
Parameters:
| Parameter | Type | Description |
|---|---|---|
form_id | str | Your Forminit form ID |
data | dict | Form data (structured blocks or flat dict) |
tracking | dict | None | Optional tracking parameters |
Returns: FormResponse dict with data or error
set_user_info()
Section titled “set_user_info()”client.set_user_info(ip=None, user_agent=None, referer=None)
Sets user information for server-side submissions. Used when submitting on behalf of a user.
Parameters:
| Parameter | Type | Description |
|---|---|---|
ip | str | None | User’s IP address |
user_agent | str | None | User’s browser user agent |
referer | str | None | Referring page URL |
close()
Section titled “close()”client.close()
Closes the underlying HTTP client. Called automatically when using context manager.
Context Manager
Section titled “Context Manager”with ForminitClient(api_key="fi_your_secret_api_key") as client:
response = client.submit("YOUR_FORM_ID", data)
# client is automatically closed
Class: AsyncForminitClient
Section titled “Class: AsyncForminitClient”Asynchronous client using httpx.AsyncClient. Supports async context manager protocol.
Constructor
Section titled “Constructor”AsyncForminitClient(api_key=None, proxy_url=None, base_url="https://forminit.com")
Methods
Section titled “Methods”Same as ForminitClient, but all methods are async:
async with AsyncForminitClient(api_key="fi_your_secret_api_key") as client:
response = await client.submit("YOUR_FORM_ID", data)
Submission Formats
Section titled “Submission Formats”Structured Blocks (JSON)
Section titled “Structured Blocks (JSON)”Use the blocks format for typed, validated submissions:
response = client.submit("YOUR_FORM_ID", {
"blocks": [
{
"type": "sender",
"properties": {
"email": "john@example.com",
"firstName": "John",
"lastName": "Doe",
"phone": "+12025550123",
"company": "Acme Corp",
},
},
{
"type": "text",
"name": "message",
"value": "Hello world",
},
{
"type": "select",
"name": "plan",
"value": "Enterprise",
},
{
"type": "rating",
"name": "urgency",
"value": 4,
},
],
})
Flat Form Data
Section titled “Flat Form Data”Use fi- prefixed keys for flat dictionary submissions. The SDK sends these as URL-encoded form data:
response = client.submit("YOUR_FORM_ID", {
"fi-sender-firstName": "John",
"fi-sender-lastName": "Doe",
"fi-sender-email": "john@example.com",
"fi-text-message": "Hello world",
})
Field Naming Patterns:
| Block Type | Pattern | Example |
|---|---|---|
| Sender properties | fi-sender-{property} | fi-sender-email |
| Text | fi-text-{name} | fi-text-message |
| Number | fi-number-{name} | fi-number-quantity |
fi-email-{name} | fi-email-invitee | |
| Phone | fi-phone-{name} | fi-phone-emergency |
| URL | fi-url-{name} | fi-url-website |
| Date | fi-date-{name} | fi-date-appointment |
| Rating | fi-rating-{name} | fi-rating-satisfaction |
| Select | fi-select-{name} | fi-select-plan |
| Country | fi-country-{name} | fi-country-shipping |
Block Types
Section titled “Block Types”Sender Block (Object Block)
Section titled “Sender Block (Object Block)”Collects submitter information. Can only appear once per submission.
{
"type": "sender",
"properties": {
"email": "john@example.com", # Email address
"firstName": "John", # First name
"lastName": "Doe", # Last name
"fullName": "John Doe", # Full name
"phone": "+12025550123", # Phone (E.164 format)
"userId": "usr_123", # User ID from your system
"company": "Acme Corp", # Company name
"position": "Developer", # Job title
"address": "123 Main St", # Street address
"city": "New York", # City
"country": "US", # Country (ISO alpha-2)
"title": "Mr", # Title (Mr, Mrs, Dr, Prof)
},
}
At least one of userId, email, phone, firstName, lastName, or fullName is required.
Tracking Block (Object Block)
Section titled “Tracking Block (Object Block)”Captures UTM parameters and attribution data. Can only appear once per submission.
{
"type": "tracking",
"properties": {
"utmSource": "google",
"utmMedium": "cpc",
"utmCampaign": "spring_2026",
"utmTerm": "form backend",
"utmContent": "hero_cta",
"gclid": "EAIaIQobChMI...", # Google Ads
"fbclid": "IwAR3x...", # Facebook
"msclkid": "a1b2c3...", # Microsoft Ads
"ttclid": "E.C.P...", # TikTok
"twclid": "abc123...", # X (Twitter)
},
}
You can also pass tracking data via the tracking parameter on submit():
response = client.submit("YOUR_FORM_ID", data, tracking={
"utm_source": "google",
"utm_medium": "cpc",
"gclid": "EAIaIQobChMI...",
})
Field Blocks
Section titled “Field Blocks”Each field block requires a unique name identifier.
# Text
{"type": "text", "name": "message", "value": "Hello world"}
# Number
{"type": "number", "name": "quantity", "value": 10}
# Email
{"type": "email", "name": "invitee", "value": "friend@example.com"}
# Phone (E.164 format)
{"type": "phone", "name": "emergency", "value": "+12025550123"}
# URL
{"type": "url", "name": "website", "value": "https://example.com"}
# Date (ISO 8601)
{"type": "date", "name": "appointment", "value": "2026-01-15"}
# Rating (1-5)
{"type": "rating", "name": "satisfaction", "value": 5}
# Select (single or multi)
{"type": "select", "name": "plan", "value": "Pro"}
{"type": "select", "name": "services", "value": ["web", "seo"]}
# Country (ISO 3166-1 alpha-2)
{"type": "country", "name": "shipping", "value": "US"}
Response
Section titled “Response”Success Response
Section titled “Success Response”{
"data": {
"hashId": "7LMIBoYY74JOCp1k",
"date": "2026-01-01 21:10:24",
"blocks": {
"sender": {
"firstName": "John",
"lastName": "Doe",
"email": "john@example.com"
},
"message": "Hello world"
}
},
"redirectUrl": "https://forminit.com/thank-you"
}
| Field | Type | Description |
|---|---|---|
data.hashId | str | Unique submission identifier |
data.date | str | Submission timestamp |
data.blocks | dict | Submitted field values |
redirectUrl | str | Thank you page URL |
Error Response
Section titled “Error Response”{
"error": {
"success": False,
"error": "FI_SCHEMA_FORMAT_EMAIL",
"code": 400,
"message": "Invalid email format"
}
}
| Field | Type | Description |
|---|---|---|
error.error | str | Error code |
error.code | int | HTTP status code |
error.message | str | Human-readable message |
User Info (Server-Side Tracking)
Section titled “User Info (Server-Side Tracking)”When submitting on behalf of a user from your server, pass their IP, user agent, and referer so Forminit can capture geolocation and attribution data:
client.set_user_info(
ip=request.headers.get("X-Forwarded-For", request.remote_addr),
user_agent=request.headers.get("User-Agent"),
referer=request.headers.get("Referer"),
)
response = client.submit("YOUR_FORM_ID", data)
The SDK sends this information via X-Forwarded-For, X-Real-IP, User-Agent, and Referer headers.
Error Codes
Section titled “Error Codes”| Error Code | Status | Description |
|---|---|---|
MISSING_API_KEY | 401 | Server-side call without API key |
FORM_NOT_FOUND | 404 | Form ID does not exist |
FORM_DISABLED | 403 | Form is currently disabled |
EMPTY_SUBMISSION | 400 | No fields with values submitted |
FI_SCHEMA_FORMAT_EMAIL | 400 | Invalid email format |
FI_RULES_PHONE_INVALID | 400 | Invalid phone number format |
FI_SCHEMA_RANGE_RATING | 400 | Rating not between 1-5 |
FI_DATA_COUNTRY_INVALID | 400 | Invalid country code |
TOO_MANY_REQUESTS | 429 | Rate limit exceeded |
Rate Limits
Section titled “Rate Limits”| Authentication | Rate Limit |
|---|---|
| With API Key | 5 requests per second |
| Without API Key | 1 request per 5 seconds |
Best Practices
Section titled “Best Practices”- Store API keys in environment variables — Never hardcode keys in source code.
- Use context managers — Ensures the HTTP client is properly closed.
- Always use the sender block to identify form submitters.
- Handle errors gracefully — Check for
errorin the response. - Use E.164 format for phone numbers (e.g.,
+12025550123). - Use ISO 3166-1 alpha-2 codes for countries (e.g.,
US,GB,TR). - Call
set_user_info()when submitting on behalf of users to enable geolocation and attribution tracking.
Resources
Section titled “Resources”Was this page helpful?
Thanks for your feedback.