Floor Plan Generation API Documentation
Base URL:
https://api.ideal.house
Version: v1
Updated: 2026-08-09
📖 Overview
The Floor Plan Generation API creates one AI-generated, black-and-white, top-down, CAD-style residential concept floor plan from structured room requirements and an optional custom prompt or reference image.
The output is intended for early layout exploration. It is not a construction drawing, and generated dimensions, geometry, fixture placement, and code compliance must be reviewed by a qualified professional.
The workflow is asynchronous:
- Create a task — Submit the floor plan parameters and receive a
taskId. - Poll for results — Query the result endpoint with the
taskIduntil the task reaches a terminal status.
🔐 Authentication
All public API requests must include an API key.
| Header | Required | Value |
|---|---|---|
APIKEY | ✅ Yes | Your API key |
Content-Type | ✅ Yes for POST | application/json |
[!WARNING] Keep your API key secure. Do not expose it in client-side code or public repositories.
💰 Credits Deduction
Credits are deducted after a generation task is successfully created. If the task ultimately fails, the deducted credits are automatically refunded. Insufficient credits return error code 9051.
Model (modelType) | Output size | Credits |
|---|---|---|
Base | 1536 × 1024 | 10 |
Pro | 2496 × 1664 | 20 |
Flash is not supported by the Floor Plan API.
See Credits Deduction Reference for common billing behavior.
📌 API Endpoints
1. Create Floor Plan Task
Creates a floor plan generation task and returns a unique task ID.
Endpoint
POST /api/v1/floorPlan/generate
Request headers
| Header | Required | Description |
|---|---|---|
APIKEY | ✅ Yes | API authentication key |
Content-Type | ✅ Yes | Must be application/json |
Request body
| Field | Type | Required | Description | Default |
|---|---|---|---|---|
bedrooms | integer | ❌ No | Bedroom count from 0 to 5 | 2 |
bathrooms | number | ❌ No | Total bathroom count from 0.5 to 4, in 0.5 increments | 1.5 |
totalArea | string | ✅ Yes | Positive target total area with unit m² or ft², such as 220 m² or 1386 ft² | — |
bedroomAreaRanges | array<object> | ❌ No | Optional bedroom sizing guidance. See Bedroom Area Ranges | Derived from totalArea when omitted |
bathroomDetails | object | ❌ No | Preferences for full bathrooms only. See Bathroom Details | — |
kitchenDetails | object | ❌ No | Optional kitchen configuration. See Kitchen Details | — |
keyRooms | array<string> | ❌ No | Additional rooms or spaces. See Key Rooms | [] |
prompt | string | ❌ No | Additional layout priorities. It cannot override structured counts or hard visual constraints | "" |
refImageUrl | string | ❌ No | Publicly accessible reference image URL | "" |
modelType | string | ❌ No | Enum: Base, Pro | Base |
[!IMPORTANT] The public API currently validates
bedroomsas0–5andbathroomsas0.5–4. Values available in another client UI do not expand these server-side limits.
General request rules
- All enum values are case-sensitive and must use the English values shown in this document.
totalAreais a target total area used to guide scale and proportions; it is not treated as an exact construction dimension.- The effective custom prompt is limited to the first 800 characters when the structured image prompt is assembled.
- Structured fields take precedence over conflicting instructions in
prompt. - A successful task generates exactly one image.
📐 Total Area
totalArea contains one positive numeric value followed by an area unit.
| Unit | Example |
|---|---|
m² | 220 m² |
ft² | 1386 ft² |
Whitespace before the unit is recommended. Decimal values are accepted when positive.
Valid examples:
{
"totalArea": "200 m²"
}
{
"totalArea": "1850 ft²"
}
🛏️ Bedroom Area Ranges
bedroomAreaRanges provides relative bedroom sizing guidance. It does not request numeric area labels in the generated image.
Each item has the following shape:
| Field | Type | Required | Description |
|---|---|---|---|
name | string | ❌ No | Bedroom identity, for example Room 1 (Master) or Room 2 |
minArea | string | ❌ No | Positive minimum area |
maxArea | string | ❌ No | Positive maximum area; cannot be less than minArea |
unit | string | ❌ No | Enum: m², ft²; use the same unit as totalArea |
Explicit range example
{
"bedroomAreaRanges": [
{
"name": "Room 1 (Master)",
"minArea": "30",
"maxArea": "40",
"unit": "m²"
},
{
"name": "Room 2",
"minArea": "20",
"maxArea": "30",
"unit": "m²"
}
]
}
Rules when a non-empty array is supplied:
- Its length must equal
bedrooms. - Each supplied
minAreaandmaxAreamust be a positive numeric string. - When both values are supplied,
minArea <= maxArea. unit, when supplied, must bem²orft².- Names are preserved. Empty or null items do not provide sizing guidance.
Automatic ranges when omitted
The field may be omitted or sent as an empty array. When no item contains an effective minArea or maxArea, the structured generation path derives internal bedroom ranges from totalArea and bedrooms:
- The bedroom area budget begins at 20% of total area for one bedroom.
- The budget increases by 7.5 percentage points for each additional bedroom, capped at 50%.
- The first bedroom receives a
1.3sizing weight; every other bedroom receives a1.0weight. - Each target becomes an approximate
±10%range, rounded to whole area units. - The unit is inherited from
totalArea. - Existing non-empty room names are retained; otherwise the server uses
Room 1,Room 2, and so on.
For 200 m² and 4 bedrooms, the current derived guidance is approximately:
[
{ "name": "Room 1", "minArea": "23", "maxArea": "28", "unit": "m²" },
{ "name": "Room 2", "minArea": "18", "maxArea": "22", "unit": "m²" },
{ "name": "Room 3", "minArea": "18", "maxArea": "22", "unit": "m²" },
{ "name": "Room 4", "minArea": "18", "maxArea": "22", "unit": "m²" }
]
These values are internal proportional guidance, not guaranteed final room areas. Explicit valid ranges always take precedence over automatic ranges.
When bedrooms is 0, omit bedroomAreaRanges or send [].
🛁 Bathroom Details
bathrooms represents the total bathroom count:
- Its integer part is the number of full bathrooms.
- A
.5fraction adds one half bathroom. - Every full bathroom is prompted to include a toilet, vanity/sink, and shower or wet area.
- A half bathroom contains a toilet and vanity/sink only, with no shower or bathtub.
bathroomDetails only configures full bathrooms:
{
"bathroomDetails": {
"fullBathroomOptions": [
{
"name": "Bathroom 1",
"wetDrySeparation": "yes",
"bathtub": "required"
},
{
"name": "Bathroom 2",
"wetDrySeparation": "no",
"bathtub": "optional"
}
]
}
}
| Field | Type | Allowed values | Description |
|---|---|---|---|
name | string | Bathroom 1, Bathroom 2, etc. | Optional display identity |
wetDrySeparation | string / null | yes, no, null | Whether to show a separated wet zone |
bathtub | string / null | no, optional, required, null | Bathtub preference |
Rules:
fullBathroomOptions.lengthcannot exceedfloor(bathrooms).- The array may contain only the full bathrooms for which preferences were selected.
- A
nullvalue means not specified. - A required bathtub is additional to the standard full-bathroom fixtures; it does not replace the toilet or shower.
- Wet/dry separation is an internal partition inside a counted bathroom, not an additional bathroom.
🍳 Kitchen Details
All kitchenDetails child fields are optional. Omit the whole object when no kitchen preference is selected.
{
"kitchenDetails": {
"type": "open",
"size": "standard",
"layout": "U",
"islandType": "preparation",
"storage": "maximum",
"features": ["breakfast nook", "pantry"]
}
}
| Field | Type | Allowed values |
|---|---|---|
type | string | open, semi-open, closed |
size | string | small, standard, large, extra large |
layout | string | I, L, U, gallery |
islandType | string | no, preparation, cooking, entertainment |
storage | string | minimal, standard, maximum |
features | array<string> | eating bar, breakfast nook, pantry |
Partial configuration is valid. For example:
{
"kitchenDetails": {
"type": "semi-open"
}
}
🚪 Key Rooms
keyRooms accepts an array of these exact values:
| Value | Description |
|---|---|
walk-in closet | Dedicated walk-in closet connected to a bedroom zone |
laundry room | Dedicated laundry space |
storage room | General storage room |
utility room | Mechanical or service room |
home office | Dedicated office or study |
garage | Garage with an exterior vehicle opening and internal home access |
pantry | Pantry adjacent to the kitchen |
combined living-dining | One shared living and dining zone |
balcony | Outdoor balcony connected to living space or a primary bedroom |
The legacy Web value balcon is also accepted and normalized to balcony.
Rules:
- Blank values are ignored and duplicate values are removed.
- Selected key rooms are requested once.
- Unselected optional spaces are excluded from the generated room program.
- If
pantryappears in bothkitchenDetails.featuresandkeyRooms, only one pantry is requested.
Example:
{
"keyRooms": [
"garage",
"home office",
"combined living-dining"
]
}
🖼️ Reference Image
refImageUrl is optional and must be directly accessible by the API server.
Requirements:
- Format: JPG/JPEG, PNG, or WebP.
- Maximum file size: 20 MB.
- Minimum dimensions: 128 × 128 px.
- Maximum dimensions: 6,000 × 6,000 px. Larger images are scaled proportionally before processing.
The reference image guides layout, adjacency, proportions, or visual style. It does not override structured room counts or other hard constraints.
🤖 Model Types
| Value | Description |
|---|---|
Base | Default. Balanced generation quality, 1536 × 1024 output |
Pro | Higher-resolution 2496 × 1664 output with a longer expected generation time |
Only Base and Pro are supported.
Fields not in the public business contract
The following fields must not be relied on by public API clients:
| Field | Notes |
|---|---|
imageNumbers | The current generator always returns one image; this field is not needed |
extData | Internal Web task-group tracking metadata; public clients should omit it |
isApiCall | Determined by the API endpoint, not by the request body |
genByMember | Internal generation metadata, not a Floor Plan request field |
Removed legacy fields that must not be sent:
floorplanSetting
roomCounts
grossArea
totalAreaValue
totalAreaUnit
totalAreaType
fullBathrooms
halfBathrooms
halfBathroomRequirement
kitchenType
diningRooms
livingRooms
extras
referenceImage
hasDetailOptions
📥 Create Task Examples
Minimal request with automatic bedroom ranges
curl -X POST "https://api.ideal.house/api/v1/floorPlan/generate" \
-H "APIKEY: your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"bedrooms": 4,
"bathrooms": 2,
"totalArea": "200 m²",
"modelType": "Pro",
"prompt": "Upper floor of a two-story Saudi Arabian villa with a master bedroom, family living area, staircase landing, and balcony"
}'
Complete request
cURL
curl -X POST "https://api.ideal.house/api/v1/floorPlan/generate" \
-H "APIKEY: your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"bedrooms": 3,
"bathrooms": 2.5,
"totalArea": "220 m²",
"bedroomAreaRanges": [
{"name": "Room 1 (Master)", "minArea": "30", "maxArea": "40", "unit": "m²"},
{"name": "Room 2", "minArea": "20", "maxArea": "30", "unit": "m²"},
{"name": "Room 3", "minArea": "20", "maxArea": "30", "unit": "m²"}
],
"bathroomDetails": {
"fullBathroomOptions": [
{"name": "Bathroom 1", "wetDrySeparation": "yes", "bathtub": "required"},
{"name": "Bathroom 2", "wetDrySeparation": "no", "bathtub": "optional"}
]
},
"kitchenDetails": {
"type": "open",
"size": "standard",
"layout": "U",
"islandType": "preparation",
"storage": "maximum",
"features": ["breakfast nook", "pantry"]
},
"keyRooms": ["garage", "home office", "combined living-dining"],
"prompt": "Bright modern home with good natural lighting",
"refImageUrl": "https://example.com/reference-plan.png",
"modelType": "Pro"
}'
Java (OkHttp)
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
public class FloorPlanApiExample {
private static final String BASE_URL = "https://api.ideal.house";
private static final String API_KEY = "your_api_key_here";
public static void main(String[] args) throws Exception {
OkHttpClient client = new OkHttpClient();
String json = """
{
"bedrooms": 4,
"bathrooms": 2,
"totalArea": "200 m²",
"keyRooms": ["walk-in closet", "balcony"],
"prompt": "Upper floor with a master bedroom and family living area",
"modelType": "Pro"
}
""";
Request request = new Request.Builder()
.url(BASE_URL + "/api/v1/floorPlan/generate")
.addHeader("APIKEY", API_KEY)
.addHeader("Content-Type", "application/json")
.post(RequestBody.create(json, MediaType.parse("application/json")))
.build();
try (Response response = client.newCall(request).execute()) {
System.out.println(response.body().string());
}
}
}
Python (requests)
import requests
BASE_URL = "https://api.ideal.house"
API_KEY = "your_api_key_here"
payload = {
"bedrooms": 4,
"bathrooms": 2,
"totalArea": "200 m²",
"keyRooms": ["walk-in closet", "balcony"],
"prompt": "Upper floor with a master bedroom and family living area",
"modelType": "Pro",
}
response = requests.post(
f"{BASE_URL}/api/v1/floorPlan/generate",
headers={"APIKEY": API_KEY, "Content-Type": "application/json"},
json=payload,
)
response.raise_for_status()
print("Task ID:", response.json()["data"])
Node.js (axios)
const axios = require('axios');
const BASE_URL = 'https://api.ideal.house';
const API_KEY = 'your_api_key_here';
async function createFloorPlanTask() {
const response = await axios.post(
`${BASE_URL}/api/v1/floorPlan/generate`,
{
bedrooms: 4,
bathrooms: 2,
totalArea: '200 m²',
keyRooms: ['walk-in closet', 'balcony'],
prompt: 'Upper floor with a master bedroom and family living area',
modelType: 'Pro'
},
{
headers: {
APIKEY: API_KEY,
'Content-Type': 'application/json'
}
}
);
console.log('Task ID:', response.data.data);
return response.data.data;
}
createFloorPlanTask();
Create task success response
{
"code": 0,
"message": "success",
"data": 1234567890123456789
}
| Field | Type | Description |
|---|---|---|
code | integer | 0 indicates that the task was created successfully |
message | string | Response message |
data | long | Task ID used to poll the result endpoint |
2. Get Task Result
Returns task progress and the generated image when available.
Endpoint
GET /api/v1/floorPlan/result?taskId={taskId}
Request headers
| Header | Required | Description |
|---|---|---|
APIKEY | ✅ Yes | API authentication key |
Query parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
taskId | long | ✅ Yes | Task ID returned by the create endpoint |
Result request examples
cURL
curl -X GET "https://api.ideal.house/api/v1/floorPlan/result?taskId=1234567890123456789" \
-H "APIKEY: your_api_key_here"
Python polling
import time
import requests
BASE_URL = "https://api.ideal.house"
API_KEY = "your_api_key_here"
task_id = 1234567890123456789
while True:
response = requests.get(
f"{BASE_URL}/api/v1/floorPlan/result",
headers={"APIKEY": API_KEY},
params={"taskId": task_id},
)
response.raise_for_status()
task = response.json()["data"]
print(task["status"], task["percentage"], task["waitNumber"])
if task["status"] in ("Success", "Failed", "Termination"):
break
time.sleep(3)
if task["status"] == "Success":
print("Result URL:", task["output"]["resultUrl"])
Node.js polling
const axios = require('axios');
const BASE_URL = 'https://api.ideal.house';
const API_KEY = 'your_api_key_here';
async function pollFloorPlanResult(taskId) {
while (true) {
const response = await axios.get(
`${BASE_URL}/api/v1/floorPlan/result`,
{
headers: { APIKEY: API_KEY },
params: { taskId }
}
);
const task = response.data.data;
console.log(task.status, task.percentage, task.waitNumber);
if (['Success', 'Failed', 'Termination'].includes(task.status)) {
if (task.status === 'Success') {
console.log('Result URL:', task.output.resultUrl);
}
return task;
}
await new Promise(resolve => setTimeout(resolve, 3000));
}
}
pollFloorPlanResult('1234567890123456789');
Completed task response
{
"code": 0,
"message": "success",
"data": {
"id": 1234567890123456789,
"status": "Success",
"waitNumber": 0,
"percentage": 100,
"input": {
"bedrooms": 4,
"bathrooms": 2,
"totalArea": "200 m²",
"bedroomAreaRanges": [
{"name": "Room 1", "minArea": "23", "maxArea": "28", "unit": "m²"},
{"name": "Room 2", "minArea": "18", "maxArea": "22", "unit": "m²"},
{"name": "Room 3", "minArea": "18", "maxArea": "22", "unit": "m²"},
{"name": "Room 4", "minArea": "18", "maxArea": "22", "unit": "m²"}
],
"keyRooms": ["walk-in closet", "balcony"],
"prompt": "Upper floor with a master bedroom and family living area",
"modelType": "Pro"
},
"output": {
"resultUrl": "https://cdn.ideal.house/output/floor-plan.jpg",
"width": 2496,
"height": 1664
}
}
}
Processing response
{
"code": 0,
"message": "success",
"data": {
"id": 1234567890123456789,
"status": "Processing",
"waitNumber": 1,
"percentage": 45,
"input": {
"bedrooms": 4,
"bathrooms": 2,
"totalArea": "200 m²",
"modelType": "Pro"
},
"output": null
}
}
Failed task response
{
"code": 0,
"message": "success",
"data": {
"id": 1234567890123456789,
"status": "Failed",
"waitNumber": 0,
"percentage": 0,
"input": {
"bedrooms": 4,
"bathrooms": 2,
"totalArea": "200 m²",
"modelType": "Pro"
},
"output": null
}
}
Result fields
| Field | Type | Description |
|---|---|---|
id | long | Task ID |
status | string | Current task status |
waitNumber | integer | Number of tasks ahead in the queue; 0 means no queued tasks ahead |
percentage | integer | Approximate completion percentage from 0 to 100 |
input | object | Normalized task input, including automatically derived bedroom ranges when applicable |
output | object / null | Generated output when the task succeeds; otherwise usually null |
output.resultUrl | string | Signed URL of the generated floor plan image |
output.width | integer | Output width in pixels |
output.height | integer | Output height in pixels |
📊 Task Status
| Status | Description |
|---|---|
Unprocessed | Task has been created but has not started |
Processing | Task is being processed |
Success | Task completed and output.resultUrl is available |
Failed | Task failed |
Termination | Task was interrupted or terminated |
Poll every 3–5 seconds. See API Task Limit.
❌ Error Responses
All error responses use the common response structure:
{
"code": 1011,
"message": "bedroomAreaRanges size must match bedrooms",
"data": null
}
| Code | Name | Description | Suggested action |
|---|---|---|---|
1001 | FAILED | Generic request failure | Check the message field |
1003 | INTERNAL_ERROR | Internal server error | Retry later; contact support if it persists |
1011 | PARAM_ERROR | Invalid request parameter | Verify counts, units, enum values, and nested arrays |
5002 | API_KEY_INVALID | Invalid or missing API key | Verify the APIKEY header |
9010 | SCAN_TEXT_ERROR | Prompt failed content review | Revise the prompt |
9038 | PROHIBITED_CONTENT | Generated output contains prohibited content | Adjust the inputs and retry |
9051 | COINS_NOT_ENOUGH | Insufficient credits | Add credits and retry |
See Error Code Reference for the complete common error list.
🔄 Web Integration Notes
The authenticated Web application and the public API use different endpoints and authentication methods:
| Client | Endpoint | Authentication |
|---|---|---|
| Web application | POST /floorPlan/generate | Login token header |
| Public API | POST /api/v1/floorPlan/generate | APIKEY header |
The business field shapes are aligned, but public API clients should follow the server-side limits and public contract in this document. In particular:
- Web clients may include internal
imageNumbersandextData; public clients do not need them. - The public API determines API-call metadata from the endpoint and credentials. Request fields such as
isApiCallandgenByMemberare unnecessary. balconis accepted for compatibility and normalized tobalcony; new integrations should sendbalcony.- The current public server limits remain
0–5bedrooms and0.5–4bathrooms even if another UI temporarily presents wider selectors.
