Access Immopix features programmatically. Edit images, manage properties and integrate AI enhancements into your workflow.
Want to use Immopix in an AI assistant without writing code? That is what the Immopix connector is for.
Note
The API is included in every package. Each API call uses credits, the same way an edit in the web interface does.
The Immopix API uses token-based authentication. You can generate and revoke your API token in your account settings.
Send your token in the Authorization header with every request:
Authorization: Token your_api_token
If your connection drops, send the same request again with the same Idempotency-Key. Immopix then does not create the object or the edit job twice.
The optional header applies to POST /properties, POST /photos/upload and POST /photos/edit.
Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000
Idempotency-Replayed: true marks that response.409 idempotency_conflict.
On an error the API returns a JSON object with an error field:
{
"error": {
"code": "insufficient_credits",
"message": "Keine Credits verfügbar"
}
}
| Code | Status | Description |
|---|---|---|
invalid_token | 401 | Token is missing or invalid |
insufficient_credits | 402 | No credits available |
invalid_preset | 400 | Invalid preset key |
image_too_large | 400 | Image exceeds 20MB |
image_required | 400 | Neither image_base64 nor image_url was provided |
rate_limit_exceeded | 429 | Rate limit reached |
invalid_idempotency_key | 400 | Key is empty, too long or contains spaces |
idempotency_conflict | 409 | The same key was used with different data |
idempotency_in_progress | 409 | The first call is still running. Retry the request after the time in the Retry-After header |
conflict | 400 | presets and custom_prompt were sent together |
not_found | 404 | Resource not found |
List endpoints support cursor-based pagination with the limit (default: 20, max: 100) and cursor parameters.
{
"data": [...],
"pagination": {
"has_more": true,
"next_cursor": "abc123"
}
}
60 requests per minute per token. Above that: 429 status with a Retry-After header.
Image limits: max 20MB, formats JPEG/PNG/WebP, max 8192x8192 pixels
The usual workflow: upload the image, wait for the job, fetch the result.
import base64
import time
import uuid
import requests
API_TOKEN = "your_api_token"
headers = {"Authorization": f"Token {API_TOKEN}"}
edit_headers = {**headers, "Idempotency-Key": str(uuid.uuid4())}
# 1. Upload the image
with open("photo.jpg", "rb") as f:
image_base64 = base64.b64encode(f.read()).decode()
response = requests.post(
"https://immopix.ai/api/v1/photos/edit",
headers=edit_headers,
json={
"image_base64": image_base64,
"presets": "auto" # or: ["sunshine", "optimize"]
}
)
job = response.json()
job_id = job["job_id"]
print(f"Job started: {job_id}")
# 2. Poll the job status
while True:
response = requests.get(
f"https://immopix.ai/api/v1/jobs/{job_id}",
headers=headers
)
job = response.json()
if job["status"] == "completed":
print(f"Done: {job['result_url']}")
break
elif job["status"] == "failed":
print(f"Error: {job['error_message']}")
break
time.sleep(2)
# 1. Upload the image
curl -X POST "https://immopix.ai/api/v1/photos/edit" \
-H "Authorization: Token your_api_token" \
-H "Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000" \
-H "Content-Type: application/json" \
-d '{
"image_url": "https://example.com/photo.jpg",
"presets": "auto"
}'
# Response: {"job_id": "job_abc123", "status": "pending", ...}
# 2. Check the job status
curl "https://immopix.ai/api/v1/jobs/job_abc123" \
-H "Authorization: Token your_api_token"
Instead of presets you can also pass your own instructions:
curl -X POST "https://immopix.ai/api/v1/photos/edit" \
-H "Authorization: Token your_api_token" \
-H "Content-Type: application/json" \
-d '{
"image_url": "https://example.com/photo.jpg",
"custom_prompt": "Make the lawn green, the sky blue, remove the bins"
}'
Tip: use webhooks instead of polling to get notified when a job finishes.
Properties are containers for the photos of one property.
Lists all properties.
query | Filters by property name. Any part of the name matches and upper and lower case are the same. |
{
"data": [
{
"id": "prp_abc123",
"name": "Musterstraße 42",
"created_at": "2024-01-15T10:30:00Z",
"photo_count": 12
}
],
"pagination": {
"has_more": false,
"next_cursor": null
}
}
Creates a new property.
name * | Name of the property |
{
"id": "prp_abc123",
"name": "Musterstraße 42",
"created_at": "2024-01-15T10:30:00Z",
"photo_count": 0
}
Property details with a nested photo list.
{
"id": "prp_abc123",
"name": "Musterstraße 42",
"created_at": "2024-01-15T10:30:00Z",
"photo_count": 3,
"photos": [
{
"id": "pht_xyz789",
"title": "Wohnzimmer",
"image_type": "indoor",
"thumbnail_url": "https://..."
}
]
}
Updates a property. Parameter: name
Deletes the property and all photos that belong to it. This cannot be undone.
Lists all photos of a property.
image_type | Filters by image type. Values: indoor, outdoor, floorplan, other. Any other value returns 400 validation_error. |
{
"data": [
{
"id": "pht_xyz789",
"title": "Wohnzimmer",
"image_type": "indoor",
"current_url": "https://...",
"thumbnail_url": "https://...",
"created_at": "2024-01-15T10:30:00Z"
}
],
"pagination": {
"has_more": false,
"next_cursor": null
}
}
Photo details with revision history and URLs.
{
"id": "pht_xyz789",
"title": "Wohnzimmer",
"image_type": "indoor",
"current_url": "https://...",
"thumbnail_url": "https://...",
"revisions": [
{
"id": "rev_abc123",
"version": 1,
"is_original": false,
"url": "https://...",
"created_at": "2024-01-15T10:35:00Z"
}
],
"created_at": "2024-01-15T10:30:00Z"
}
Sends an image for AI enhancement.
image_base64 | Base64-encoded image data |
image_url | URL of the image (publicly reachable) |
photo_id | Edit an existing photo again |
presets | Array of preset keys or "auto". GET /presets returns all keys. |
custom_prompt | Your own instructions (max. 1000 characters). Core rules are applied automatically. |
property_id | Link the photo to a property |
edit_mode | "outdoor" (default), "indoor" or "floorplan" |
lock_camera_angle | Keep the camera angle (boolean) |
room_type | Room type for furnishing. Values: living_room, bedroom, kitchen, bathroom, kids_room, dining_room, hallway, office, garage, basement, dressing_room. Detected automatically when you leave it out. |
furniture_style | Furniture style for staging. Values: modern, skandinavisch, klassisch, landhausstil, industriell. Detected automatically when you leave it out. |
intensity | "normal" (default) or "subtle" for a more restrained edit. |
resolution | "2K" (default) or "4K". 4K uses 2 credits. |
You must provide image_base64, image_url or photo_id.
presets and custom_prompt are mutually exclusive, so use one of the two. Without either, the default presets apply.
The current values for presets, room_type and furniture_style come from GET /presets. Note that furniture_style keys are German words.
Use "auto" for automatic detection, or pick specific presets. GET /presets returns the complete, current list.
{
"job_id": "job_abc123",
"photo_id": "pht_xyz789",
"status": "pending"
}
Uploads a photo without starting an edit. No credits are used. The photo is stored, categorised and ready to edit in the dashboard.
image_base64 | Base64-encoded image data |
image_url | URL of the image (publicly reachable) |
property_id | Link the photo to a property (optional, default: "API Uploads") |
You must provide image_base64 or image_url (exactly one).
{
"photo_id": "pht_xyz789",
"property_id": "prp_abc123",
"property_url": "https://immopix.ai/app/property/prp_abc123/"
}
Deletes the photo and all its revisions.
Check the job status. Status: pending | processing | completed | failed
{
"job_id": "job_abc123",
"photo_id": "pht_xyz789",
"status": "completed",
"presets": ["sunshine", "optimize"],
"result_url": "https://...",
"width": 1920,
"height": 1080,
"error_message": null,
"created_at": "2024-01-15T10:30:00Z"
}
result_url, width, height only with status: "completed".
error_message only with status: "failed".
This endpoint returns every option an edit accepts. That keeps your integration current, without hard-coding the values.
Lists all presets, furniture styles and room types.
The key values are stable identifiers, while label and description are returned in German.
{
"presets": [
{
"key": "optimize",
"edit_mode": "outdoor",
"label": "Belichtung optimieren",
"description": "Helligkeit, Farben & Kontrast verbessern"
},
{
"key": "staging",
"edit_mode": "indoor",
"label": "Virtuell möblieren",
"description": "Leere Räume mit passenden Möbeln einrichten"
},
{
"key": "floorplan_3d",
"edit_mode": "floorplan",
"label": "3D Grundriss",
"description": "2D-Grundriss in eine 3D-Ansicht umwandeln"
}
...
],
"furniture_styles": ["modern", "skandinavisch", ...],
"room_types": [
{"key": "living_room", "label": "Wohnzimmer"},
{"key": "bedroom", "label": "Schlafzimmer"}
...
]
}
You put the key values straight into POST /api/v1/photos/edit: presets from presets, room_type from room_types and furniture_style from furniture_styles.
edit_mode assigns each preset to one mode: outdoor, indoor or floorplan. Use only presets from the mode you send in the request.
Without presets, or with "auto", Immopix picks suitable presets for the image.
You configure webhook URLs in your account settings. Events: job.completed, job.failed
{
"event": "job.completed",
"job": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"photo_id": "pht_xyz789",
"status": "completed",
"presets": ["sunshine", "optimize"],
"result_url": "https://...",
"width": 1920,
"height": 1080,
"created_at": "2024-01-15T10:30:00Z"
}
}
Every webhook carries an X-Immopix-Signature header (HMAC-SHA256).
import hmac
import hashlib
def verify_signature(payload: bytes, signature: str, secret: str) -> bool:
expected = hmac.new(secret.encode(), payload, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, signature)
# In your webhook handler:
signature = request.headers.get("X-Immopix-Signature")
if not verify_signature(request.data, signature, WEBHOOK_SECRET):
return "Invalid signature", 401
const crypto = require('crypto');
function verifySignature(payload, signature, secret) {
const expected = crypto.createHmac('sha256', secret).update(payload).digest('hex');
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature));
}
The API uses the same credit system as the web interface. You find the details on our pricing page. An insufficient_credits error means your credits are used up.
Reply within 2 hours
We will get back to you within 2 hours.