Multi-File Upload API Documentation
Version: 2.0 Last Updated: November 28, 2025 Status: Production Ready
Overview
The Multi-File Upload API enables uploading multiple documents (up to 50 PDFs) in a single request with intelligent auto-categorization and selective analysis. This feature dramatically reduces API calls and speeds up document processing.
Key Benefits
- Batch Upload: Upload up to 50 PDFs in one request
- Instant Categorization: Automatic document type detection using 60+ filename patterns
- Smart Analysis: Only critical/important documents are auto-analyzed (saves 74% of API calls)
- On-Demand Analysis: Optional documents analyzed only when user asks about them
- 6-9x Faster: Parallel processing vs sequential uploads
Endpoint
POST /v1/properties/{property_id}/upload
Upload multiple PDF documents to a property for analysis.
Authentication: Required (Bearer token)
Content-Type: multipart/form-data
Rate Limits:
- Maximum 50 files per request
- Files must be PDF format
- Recommended max file size: 50 MB per file
Request
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
property_id | string (UUID) | Yes | The property ID to upload documents to |
Form Data
| Field | Type | Required | Description |
|---|---|---|---|
files | File[] | Yes | Array of PDF files (1-50 files) |
cURL Example
curl -X POST "https://api.homeinsightai.com/v1/properties/{property_id}/upload" \
-H "Authorization: Bearer hi_live_abc123xyz..." \
-F "files=@Home_Inspection_Report.pdf" \
-F "files=@Loan_Estimate_2024.pdf" \
-F "files=@Seller_Disclosure.pdf" \
-F "files=@Purchase_Agreement.pdf"
Python Example
import requests
url = "https://api.homeinsightai.com/v1/properties/{property_id}/upload"
headers = {
"Authorization": "Bearer hi_live_abc123xyz..."
}
files = [
("files", ("Home_Inspection_Report.pdf", open("inspection.pdf", "rb"), "application/pdf")),
("files", ("Loan_Estimate_2024.pdf", open("loan.pdf", "rb"), "application/pdf")),
("files", ("Seller_Disclosure.pdf", open("disclosure.pdf", "rb"), "application/pdf")),
]
response = requests.post(url, headers=headers, files=files)
print(response.json())
JavaScript Example
const formData = new FormData();
formData.append('files', inspectionFile);
formData.append('files', loanFile);
formData.append('files', disclosureFile);
const response = await fetch(
`https://api.homeinsightai.com/v1/properties/${propertyId}/upload`,
{
method: 'POST',
headers: {
'Authorization': `Bearer ${apiKey}`,
},
body: formData,
}
);
const result = await response.json();
console.log(result);
Response
Success Response (200 OK)
{
"property_id": "550e8400-e29b-41d4-a716-446655440000",
"documents_uploaded": 4,
"documents_categorized": 4,
"auto_analyzed": 0,
"queued_for_analysis": 2,
"documents": [
{
"id": "ana_1a2b3c4d5e6f",
"filename": "Home_Inspection_Report.pdf",
"category": "critical",
"is_analyzed": false,
"file_size_bytes": 2458923,
"confidence": 0.95,
"categorization_method": "filename_pattern",
"reasoning": "Filename matches critical patterns: (?i)inspection.*report, (?i)home.*inspection"
},
{
"id": "ana_2b3c4d5e6f7g",
"filename": "Loan_Estimate_2024.pdf",
"category": "important",
"is_analyzed": false,
"file_size_bytes": 845672,
"confidence": 0.90,
"categorization_method": "filename_pattern",
"reasoning": "Filename matches important patterns: (?i)loan.*estimate"
},
{
"id": "ana_3c4d5e6f7g8h",
"filename": "Seller_Disclosure.pdf",
"category": "critical",
"is_analyzed": false,
"file_size_bytes": 1234567,
"confidence": 0.95,
"categorization_method": "filename_pattern",
"reasoning": "Filename matches critical patterns: (?i)seller.*disclosure"
},
{
"id": "ana_4d5e6f7g8h9i",
"filename": "Purchase_Agreement.pdf",
"category": "optional",
"is_analyzed": false,
"file_size_bytes": 987654,
"confidence": 0.80,
"categorization_method": "filename_pattern",
"reasoning": "Filename matches optional patterns: (?i)purchase.*agreement"
}
],
"failed_documents": []
}
Response Fields
| Field | Type | Description |
|---|---|---|
property_id | string | Property UUID |
documents_uploaded | integer | Number of successfully uploaded documents |
documents_categorized | integer | Number of documents successfully categorized |
auto_analyzed | integer | Number of documents analyzed immediately (always 0 on upload, updated by background tasks) |
queued_for_analysis | integer | Number of critical/important documents queued for background analysis |
documents | array | List of uploaded document details |
failed_documents | array | List of failed uploads with error messages |
Document Object
| Field | Type | Description |
|---|---|---|
id | string | Analysis ID (format: ana_xxxxx) |
filename | string | Original filename |
category | string | Auto-detected category: critical, important, optional, noise |
is_analyzed | boolean | Whether full analysis is complete (false on upload, updated by background task) |
file_size_bytes | integer | File size in bytes |
confidence | float | Categorization confidence (0.0-1.0) |
categorization_method | string | How it was categorized: filename_pattern or ai_scan |
reasoning | string | Why this category was assigned |
Failed Document Object
| Field | Type | Description |
|---|---|---|
filename | string | Original filename |
error | string | Error message describing why upload failed |
Error Responses
400 Bad Request
No files provided:
{
"detail": "No files provided"
}
Too many files:
{
"detail": "Maximum 50 files allowed per upload"
}
Invalid file type:
{
"detail": "Only PDF files allowed. Invalid file: document.docx"
}
401 Unauthorized
{
"detail": "Invalid or missing API key"
}
404 Not Found
{
"detail": "Property not found"
}
500 Internal Server Error
{
"detail": "Failed to create record for filename.pdf"
}
Document Categories
Documents are automatically categorized into four tiers:
Critical (Auto-Analyzed)
Documents that MUST be analyzed for user safety and legal compliance.
Examples:
- Home inspection reports
- Property inspection reports
- Structural reports
- Pest/termite inspections
- Seller disclosures
- Property disclosures
- Natural hazard disclosures
- Lead-based paint disclosures
- Environmental disclosures
Auto-Analysis: ✅ Yes (queued immediately)
Important (Auto-Analyzed)
Documents that provide significant value but aren't safety-critical.
Examples:
- Loan estimates
- Closing disclosures
- Mortgage applications
- Pre-approvals
- Appraisal reports
- Property valuations
- Title reports
- Preliminary title documents
Auto-Analysis: ✅ Yes (queued immediately)
Optional (On-Demand)
Documents analyzed only when user asks about them.
Examples:
- Purchase agreements
- Sales contracts
- Listing agreements
- Addendums
- Amendments
- Buyer representation forms
- Agency disclosures
- Escrow instructions
Auto-Analysis: ❌ No (analyzed when user asks)
Noise (Skipped)
Administrative documents that don't require AI analysis.
Examples:
- Receipts
- Acknowledgements
- Notices to perform
- Contingency removals
- HOA rules
- CC&Rs (Covenants, Conditions & Restrictions)
- Calendars
- Schedules
- Checklists
Auto-Analysis: ❌ No (metadata only)
Background Processing
How It Works
-
Upload Phase (instant):
- Files uploaded to server
- Database records created with
is_analyzed: false - Documents categorized using filename patterns (free, instant)
- Response returned to client immediately
-
Background Analysis Phase (15-30 seconds per document):
- Critical/important documents queued for processing
- PDF text and images extracted
- Gemini Vision analysis runs in parallel
- Embeddings generated and stored
- Database updated with
is_analyzed: true
-
On-Demand Phase (triggered by user questions):
- Chat endpoint detects questions about unanalyzed documents
- Relevant optional documents queued for analysis
- User notified analysis is in progress
Checking Analysis Status
Use the property documents endpoint to check which documents have completed analysis:
curl -X GET "https://api.homeinsightai.com/v1/properties/{property_id}/documents" \
-H "Authorization: Bearer hi_live_abc123xyz..."
Response:
{
"property_id": "550e8400-e29b-41d4-a716-446655440000",
"documents": [
{
"id": "ana_1a2b3c4d5e6f",
"document_type": "inspection_report",
"status": "completed",
"filename": "Home_Inspection_Report.pdf",
"property_address": "123 Main St",
"created_at": "2025-11-28T10:30:00Z"
}
],
"total": 1
}
Performance Benchmarks
Multi-File Upload Speed
| Scenario | Before (Sequential) | After (Parallel) | Improvement |
|---|---|---|---|
| 3 PDFs (10 images each) | 90-204s | 15-23s | 6-9x faster |
| 10 PDFs | ~300-600s | ~30-60s | 10x faster |
| 50 PDFs | ~1500-3000s | ~150-300s | 10x faster |
Resource Efficiency
| Scenario | Old Approach | New Approach | Savings |
|---|---|---|---|
| 50 documents uploaded | 500 API calls | 130 API calls | 74% fewer calls |
| Analysis breakdown | All analyzed | 7 critical + 5 on-demand | 5x more efficient |
Best Practices
1. Use Descriptive Filenames
The categorization system relies on filename patterns. Use clear, descriptive filenames:
✅ Good:
Home_Inspection_Report_123_Main_St.pdfLoan_Estimate_Wells_Fargo_2024.pdfSeller_Property_Disclosure.pdf
❌ Bad:
document1.pdfscan.pdfuntitled.pdf
2. Group Related Documents
Upload all documents for a property in a single batch when possible:
// Good - Single batch upload
const allFiles = [
inspectionReport,
loanEstimate,
disclosure,
appraisal,
purchaseAgreement
];
await uploadDocuments(propertyId, allFiles);
// Less efficient - Multiple uploads
await uploadDocuments(propertyId, [inspectionReport]);
await uploadDocuments(propertyId, [loanEstimate]);
await uploadDocuments(propertyId, [disclosure]);
3. Handle Partial Failures
Always check the failed_documents array and handle errors gracefully:
const result = await uploadDocuments(propertyId, files);
if (result.failed_documents.length > 0) {
console.warn(`${result.failed_documents.length} documents failed to upload:`);
result.failed_documents.forEach(failed => {
console.error(`- ${failed.filename}: ${failed.error}`);
});
// Optionally retry failed uploads
await retryFailedUploads(result.failed_documents);
}
4. Monitor Background Processing
For critical workflows, poll the documents endpoint to confirm analysis completion:
async function waitForAnalysis(propertyId, documentIds, timeout = 60000) {
const startTime = Date.now();
while (Date.now() - startTime < timeout) {
const docs = await getPropertyDocuments(propertyId);
const analyzed = docs.documents.filter(d =>
documentIds.includes(d.id) && d.status === 'completed'
);
if (analyzed.length === documentIds.length) {
return true; // All documents analyzed
}
await sleep(5000); // Check every 5 seconds
}
throw new Error('Analysis timeout');
}
5. Optimize File Sizes
Compress PDFs before upload to reduce bandwidth and processing time:
- Use PDF compression tools (Ghostscript, Adobe Acrobat)
- Target file sizes under 10 MB when possible
- Downscale images to 1920x1080 or lower
- Remove unnecessary pages
Migration Guide
Upgrading from Single-File Upload
Old API (still supported):
// POST /v1/analyses
const response = await fetch('/v1/analyses', {
method: 'POST',
body: formData, // Single file
});
New API (recommended):
// POST /v1/properties/{property_id}/upload
const response = await fetch(`/v1/properties/${propertyId}/upload`, {
method: 'POST',
body: formData, // Multiple files
});
Migration Checklist:
- ✅ Create a property first (if not already created)
- ✅ Update upload endpoint from
/v1/analysesto/v1/properties/{id}/upload - ✅ Support multiple file selection in UI
- ✅ Handle
documentsarray in response (instead of single document) - ✅ Handle
failed_documentsarray for error reporting - ✅ Update status checking to use
/v1/properties/{id}/documents - ✅ Test with batch uploads of 5-10 files
Backward Compatibility
The old single-file upload endpoint (POST /v1/analyses) remains available for backward compatibility. However, new integrations should use the multi-file endpoint for better performance.
Troubleshooting
Problem: Files Not Categorizing Correctly
Solution: Use more descriptive filenames that match the patterns:
# Check categorization patterns
# Critical: inspection, disclosure, structural, pest
# Important: loan, appraisal, title
# Optional: purchase, agreement, contract
# Noise: receipt, acknowledgement, hoa, rules
Problem: Analysis Taking Too Long
Causes:
- Large files (>20 MB)
- High image count (>50 images per PDF)
- Server under heavy load
Solutions:
- Compress PDFs before upload
- Split large documents into smaller files
- Check system status at status.homeinsightai.com
Problem: Some Documents Failed to Upload
Check failed_documents array:
{
"failed_documents": [
{
"filename": "corrupted.pdf",
"error": "Failed to create record for corrupted.pdf"
}
]
}
Common Causes:
- Corrupted PDF files
- Password-protected PDFs
- Invalid PDF structure
- Network timeout
Solutions:
- Re-save PDF from source application
- Remove password protection
- Use PDF repair tools
- Retry upload
Rate Limits
| Plan | Upload Limit | Files Per Request | Max File Size |
|---|---|---|---|
| Free Trial | 1 property/month | 10 files | 50 MB |
| Pay-Per-Report ($29/property) | 1 property per purchase | 50 files | 50 MB |
| Pro ($99/mo) | Unlimited properties/month | 75 files | 100 MB |
| Enterprise ($999/mo + usage) | Unlimited | Unlimited | Unlimited |
Support
- Documentation: https://docs.homeinsightai.com
- API Status: https://status.homeinsightai.com
- Email: support@homeinsightai.com
- Discord: https://discord.gg/homeinsightai