Docs

API Documentation

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:

  1. Create a task — Submit the floor plan parameters and receive a taskId.
  2. Poll for results — Query the result endpoint with the taskId until the task reaches a terminal status.

🔐 Authentication

All public API requests must include an API key.

HeaderRequiredValue
APIKEY✅ YesYour API key
Content-Type✅ Yes for POSTapplication/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 sizeCredits
Base1536 × 102410
Pro2496 × 166420

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

HeaderRequiredDescription
APIKEY✅ YesAPI authentication key
Content-Type✅ YesMust be application/json

Request body

FieldTypeRequiredDescriptionDefault
bedroomsinteger❌ NoBedroom count from 0 to 52
bathroomsnumber❌ NoTotal bathroom count from 0.5 to 4, in 0.5 increments1.5
totalAreastring✅ YesPositive target total area with unit or ft², such as 220 m² or 1386 ft²
bedroomAreaRangesarray<object>❌ NoOptional bedroom sizing guidance. See Bedroom Area RangesDerived from totalArea when omitted
bathroomDetailsobject❌ NoPreferences for full bathrooms only. See Bathroom Details
kitchenDetailsobject❌ NoOptional kitchen configuration. See Kitchen Details
keyRoomsarray<string>❌ NoAdditional rooms or spaces. See Key Rooms[]
promptstring❌ NoAdditional layout priorities. It cannot override structured counts or hard visual constraints""
refImageUrlstring❌ NoPublicly accessible reference image URL""
modelTypestring❌ NoEnum: Base, ProBase

[!IMPORTANT] The public API currently validates bedrooms as 0–5 and bathrooms as 0.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.
  • totalArea is 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.

UnitExample
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:

FieldTypeRequiredDescription
namestring❌ NoBedroom identity, for example Room 1 (Master) or Room 2
minAreastring❌ NoPositive minimum area
maxAreastring❌ NoPositive maximum area; cannot be less than minArea
unitstring❌ NoEnum: , 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 minArea and maxArea must be a positive numeric string.
  • When both values are supplied, minArea <= maxArea.
  • unit, when supplied, must be or ft².
  • 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.3 sizing weight; every other bedroom receives a 1.0 weight.
  • 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 .5 fraction 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"
      }
    ]
  }
}
FieldTypeAllowed valuesDescription
namestringBathroom 1, Bathroom 2, etc.Optional display identity
wetDrySeparationstring / nullyes, no, nullWhether to show a separated wet zone
bathtubstring / nullno, optional, required, nullBathtub preference

Rules:

  • fullBathroomOptions.length cannot exceed floor(bathrooms).
  • The array may contain only the full bathrooms for which preferences were selected.
  • A null value 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"]
  }
}
FieldTypeAllowed values
typestringopen, semi-open, closed
sizestringsmall, standard, large, extra large
layoutstringI, L, U, gallery
islandTypestringno, preparation, cooking, entertainment
storagestringminimal, standard, maximum
featuresarray<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:

ValueDescription
walk-in closetDedicated walk-in closet connected to a bedroom zone
laundry roomDedicated laundry space
storage roomGeneral storage room
utility roomMechanical or service room
home officeDedicated office or study
garageGarage with an exterior vehicle opening and internal home access
pantryPantry adjacent to the kitchen
combined living-diningOne shared living and dining zone
balconyOutdoor 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 pantry appears in both kitchenDetails.features and keyRooms, 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

ValueDescription
BaseDefault. Balanced generation quality, 1536 × 1024 output
ProHigher-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:

FieldNotes
imageNumbersThe current generator always returns one image; this field is not needed
extDataInternal Web task-group tracking metadata; public clients should omit it
isApiCallDetermined by the API endpoint, not by the request body
genByMemberInternal 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
}
FieldTypeDescription
codeinteger0 indicates that the task was created successfully
messagestringResponse message
datalongTask 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

HeaderRequiredDescription
APIKEY✅ YesAPI authentication key

Query parameters

ParameterTypeRequiredDescription
taskIdlong✅ YesTask 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

FieldTypeDescription
idlongTask ID
statusstringCurrent task status
waitNumberintegerNumber of tasks ahead in the queue; 0 means no queued tasks ahead
percentageintegerApproximate completion percentage from 0 to 100
inputobjectNormalized task input, including automatically derived bedroom ranges when applicable
outputobject / nullGenerated output when the task succeeds; otherwise usually null
output.resultUrlstringSigned URL of the generated floor plan image
output.widthintegerOutput width in pixels
output.heightintegerOutput height in pixels

📊 Task Status

StatusDescription
UnprocessedTask has been created but has not started
ProcessingTask is being processed
SuccessTask completed and output.resultUrl is available
FailedTask failed
TerminationTask 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
}
CodeNameDescriptionSuggested action
1001FAILEDGeneric request failureCheck the message field
1003INTERNAL_ERRORInternal server errorRetry later; contact support if it persists
1011PARAM_ERRORInvalid request parameterVerify counts, units, enum values, and nested arrays
5002API_KEY_INVALIDInvalid or missing API keyVerify the APIKEY header
9010SCAN_TEXT_ERRORPrompt failed content reviewRevise the prompt
9038PROHIBITED_CONTENTGenerated output contains prohibited contentAdjust the inputs and retry
9051COINS_NOT_ENOUGHInsufficient creditsAdd 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:

ClientEndpointAuthentication
Web applicationPOST /floorPlan/generateLogin token header
Public APIPOST /api/v1/floorPlan/generateAPIKEY 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 imageNumbers and extData; public clients do not need them.
  • The public API determines API-call metadata from the endpoint and credentials. Request fields such as isApiCall and genByMember are unnecessary.
  • balcon is accepted for compatibility and normalized to balcony; new integrations should send balcony.
  • The current public server limits remain 0–5 bedrooms and 0.5–4 bathrooms even if another UI temporarily presents wider selectors.