CloseDaily API Documentation
The CloseDaily API lets you programmatically manage your blog posts, categories, media, and access your agent profile. Use it to automate content publishing, integrate with AI writing tools like Claude, or build custom workflows.
Authentication
All API requests require your API key sent in the x-api-key header. You can find your API key in Website Settings → Integrations inside your CloseDaily dashboard.
curl -H "x-api-key: YOUR_API_KEY" \
https://app.closedaily.com/api/v1/blog/posts
Base URL: https://app.closedaily.com
Quick Start
Upload an image and create a published blog post in two API calls:
Step 1 — Upload a featured image
Option A — From a URL (recommended for AI agents):
curl -X POST "https://app.closedaily.com/api/v1/media/upload-url" \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com/my-photo.jpg", "alt_text": "Beautiful Nashville home"}'
Option B — From a local file:
curl -X POST "https://app.closedaily.com/api/v1/media/upload" \
-H "x-api-key: YOUR_API_KEY" \
-F "file=@my-photo.jpg" \
-F "alt_text=Beautiful Nashville home"
Response:
{
"id": 87,
"url": "https://app.closedaily.com/api/website/media/images/1/171...-a3x.jpg",
"filename": "my-photo.jpg",
"file_type": "image",
"mime_type": "image/jpeg",
"file_size": 245000
}
Step 2 — Create the post using the returned image URL
curl -X POST "https://app.closedaily.com/api/v1/blog/posts" \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"title": "Top Neighborhoods in Nashville",
"content_html": "<h2>The Gulch</h2><p>Modern condos...</p>",
"featured_image": "https://app.closedaily.com/api/website/media/images/1/171...-a3x.jpg",
"tags": ["nashville", "neighborhoods"],
"status": "published"
}'
Agent Profile
Get Agent Profile
GET /api/v1/profile
Returns your agent profile, service areas, and website info. Use this to give AI writers (like Claude) full context about who you are, what areas you serve, and your website details so they can write hyper-relevant content.
curl -H "x-api-key: YOUR_API_KEY" \
"https://app.closedaily.com/api/v1/profile"
Response:
{
"agent": {
"first_name": "John",
"last_name": "Smith",
"email": "john@example.com",
"phone": "(615) 555-1234",
"brokerage": "Keller Williams",
"brokerage_logo_url": "",
"license_number": "12345",
"license_state": "TN",
"specialties": "Luxury homes, First-time buyers",
"bio": "Top-producing agent in Nashville..."
},
"service_areas": [
{ "id": "12345", "name": "Nashville", "stateAbrv": "TN" },
{ "id": "12346", "name": "Franklin", "stateAbrv": "TN" },
{ "id": "12347", "name": "Brentwood", "stateAbrv": "TN" }
],
"website": {
"site_name": "John Smith Real Estate",
"tagline": "Your Nashville Home Expert",
"domain": "johnsmith.closedailyagent.com",
"url": "https://johnsmith.closedailyagent.com"
}
}
The service_areas array contains the cities/markets the agent serves. AI tools should use these to write location-specific content, reference local neighborhoods, and include relevant internal links.
Blog Posts
List Posts
GET /api/v1/blog/posts
Retrieve a paginated list of your blog posts.
| Parameter | Type | Required | Description |
|---|---|---|---|
status |
string | No | Filter by status: draft, published, scheduled, archived |
category_id |
number | No | Filter by category ID |
search |
string | No | Search in title and excerpt |
page |
number | No | Page number (default: 1) |
limit |
number | No | Results per page, max 100 (default: 20) |
curl -H "x-api-key: YOUR_API_KEY" \
"https://app.closedaily.com/api/v1/blog/posts?status=published&limit=10"
Response:
{
"posts": [
{
"id": 42,
"title": "10 Tips for First-Time Home Buyers",
"slug": "10-tips-first-time-home-buyers",
"excerpt": "Everything you need to know...",
"featured_image": "https://app.closedaily.com/...",
"status": "published",
"category_id": 3,
"tags": "[\"real estate\",\"tips\"]",
"views": 128,
"read_time_minutes": 5,
"published_at": "2026-03-15T14:00:00.000Z"
}
],
"total": 42,
"page": 1,
"totalPages": 3
}
Get Single Post
GET /api/v1/blog/posts/:id
Get a single blog post by ID, including full content (content_html, content_json).
curl -H "x-api-key: YOUR_API_KEY" \
"https://app.closedaily.com/api/v1/blog/posts/42"
Create Post
POST /api/v1/blog/posts
Create a new blog post. Only title is required — all other fields are optional.
| Parameter | Type | Required | Description |
|---|---|---|---|
title |
string | Yes | Post title |
slug |
string | No | URL slug (auto-generated from title if omitted) |
content_html |
string | No | Post body as HTML |
content_json |
string | No | Post body as TipTap JSON (for editor compatibility) |
excerpt |
string | No | Short summary / meta description |
featured_image |
string | No | URL of the featured image |
category_id |
number | No | Category ID |
tags |
array | No | Array of tag strings, e.g. [“real estate”, “tips”] |
status |
string | No | draft (default), published, or scheduled |
seo_title |
string | No | Custom SEO title (max 60 chars recommended) |
seo_description |
string | No | Custom meta description (max 160 chars recommended) |
og_image |
string | No | Open Graph image URL for social sharing |
scheduled_at |
string | No | ISO 8601 datetime to auto-publish (requires status: “scheduled”) |
curl -X POST "https://app.closedaily.com/api/v1/blog/posts" \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"title": "Nashville Housing Market Update",
"content_html": "<h2>Market Overview</h2><p>The Nashville market...</p>",
"excerpt": "A look at current trends in Nashville real estate.",
"featured_image": "https://app.closedaily.com/api/website/media/images/1/photo.jpg",
"tags": ["nashville", "market update"],
"status": "published",
"seo_title": "Nashville Housing Market Update | March 2026"
}'
Update Post
PUT /api/v1/blog/posts/:id
Update an existing blog post. Only send the fields you want to change.
| Parameter | Type | Required | Description |
|---|---|---|---|
title |
string | No | Post title |
slug |
string | No | URL slug |
content_html |
string | No | Post body as HTML |
featured_image |
string | No | Featured image URL |
category_id |
number | No | Category ID |
tags |
array | No | Array of tag strings |
status |
string | No | draft, published, scheduled, or archived |
seo_title |
string | No | Custom SEO title |
seo_description |
string | No | Custom meta description |
faq_schema |
array | No | FAQ structured data: [{question, answer}, …] |
curl -X PUT "https://app.closedaily.com/api/v1/blog/posts/42" \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "title": "Updated: 10 Tips for Home Buyers", "tags": ["updated", "tips"] }'
Delete Post
DELETE /api/v1/blog/posts/:id
Permanently delete a blog post.
curl -X DELETE "https://app.closedaily.com/api/v1/blog/posts/42" \
-H "x-api-key: YOUR_API_KEY"
Response: { "success": true }
Publish Post
PUT /api/v1/blog/posts/:id/publish
Publish a post immediately. Sets status to “published” and records published_at timestamp.
curl -X PUT "https://app.closedaily.com/api/v1/blog/posts/42/publish" \
-H "x-api-key: YOUR_API_KEY"
Unpublish Post
PUT /api/v1/blog/posts/:id/unpublish
Revert a published post back to draft status.
curl -X PUT "https://app.closedaily.com/api/v1/blog/posts/42/unpublish" \
-H "x-api-key: YOUR_API_KEY"
Categories
List Categories
GET /api/v1/blog/categories
curl -H "x-api-key: YOUR_API_KEY" \
"https://app.closedaily.com/api/v1/blog/categories"
Response:
{
"categories": [
{ "id": 1, "name": "Market Updates", "slug": "market-updates" },
{ "id": 2, "name": "Home Buying Tips", "slug": "home-buying-tips" }
]
}
Create Category
POST /api/v1/blog/categories
| Parameter | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | Category name (slug auto-generated) |
curl -X POST "https://app.closedaily.com/api/v1/blog/categories" \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "name": "Neighborhood Guides" }'
Delete Category
DELETE /api/v1/blog/categories/:id
Delete a blog category. Posts in this category will become uncategorized.
curl -X DELETE "https://app.closedaily.com/api/v1/blog/categories/5" \
-H "x-api-key: YOUR_API_KEY"
Internal Links
Get Internal Links
GET /api/v1/blog/internal-links
Returns all published posts and pages with their full URLs. Use this for internal linking, content planning, and SEO.
curl -H "x-api-key: YOUR_API_KEY" \
"https://app.closedaily.com/api/v1/blog/internal-links"
Response:
{
"links": [
{
"id": 1,
"title": "Home",
"url": "https://yoursite.closedailyagent.com/",
"type": "page",
"slug": "home"
},
{
"id": 42,
"title": "10 Tips for Home Buyers",
"url": "https://yoursite.closedailyagent.com/10-tips/",
"type": "post",
"slug": "10-tips",
"excerpt": "Everything you need..."
}
],
"domain": "yoursite.closedailyagent.com"
}
Media
Upload Media
POST /api/v1/media/upload
Upload an image, video, or document. Send as multipart/form-data with field name file.
| Parameter | Type | Required | Description |
|---|---|---|---|
file |
file | Yes | The file to upload (multipart/form-data) |
alt_text |
string | No | Alt text for images (accessibility + SEO) |
folder |
string | No | Organizational folder name (default: “general”) |
curl -X POST "https://app.closedaily.com/api/v1/media/upload" \
-H "x-api-key: YOUR_API_KEY" \
-F "file=@photo.jpg" \
-F "alt_text=Nashville skyline at sunset"
Response:
{
"id": 87,
"url": "https://app.closedaily.com/api/website/media/images/1/1710756000-a3x9f2.jpg",
"filename": "photo.jpg",
"file_type": "image",
"mime_type": "image/jpeg",
"file_size": 245000
}
The returned url is permanent and can be used directly as a featured_image value when creating or updating posts.
Upload Media from URL
POST /api/v1/media/upload-url
Download an image from a URL and save it to your media library. Recommended for AI agents — no multipart/form-data needed, just send JSON.
| Parameter | Type | Required | Description |
|---|---|---|---|
url |
string | Yes | The image URL to download |
alt_text |
string | No | Alt text for the image |
folder |
string | No | Organizational folder name (default: “general”) |
curl -X POST "https://app.closedaily.com/api/v1/media/upload-url" \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com/photo.jpg", "alt_text": "Beautiful home at sunset"}'
Response:
{
"id": 88,
"url": "https://app.closedaily.com/api/website/media/images/1/1710756100-b7k2m9.jpg",
"filename": "photo.jpg",
"file_type": "image",
"mime_type": "image/jpeg",
"file_size": 312000
}
Workflow for AI agents: Call upload-url with an image URL, then use the returned url as the featured_image when creating a blog post. No file handling needed.
List Media
GET /api/v1/media
List your uploaded media files with pagination and optional type filter.
| Parameter | Type | Required | Description |
|---|---|---|---|
file_type |
string | No | Filter by type: image, video, or document |
page |
number | No | Page number (default: 1) |
limit |
number | No | Results per page, max 100 (default: 50) |
curl -H "x-api-key: YOUR_API_KEY" \
"https://app.closedaily.com/api/v1/media?file_type=image&limit=20"
Important Notes
- All responses are JSON. Errors return
{ "error": "message" }with the appropriate HTTP status code. - Creating a post with
status: "published"sets the publish date automatically. - Use
status: "scheduled"with ascheduled_atdatetime (ISO 8601) to auto-publish at a future time. The scheduler checks every 60 seconds. - Read time is auto-calculated from
content_htmlword count at 200 words per minute. - Slugs are auto-generated from the title if not provided. Duplicate slugs will return an error.
- After creating or updating published posts, run a site publish from your dashboard to deploy changes to your live website.
- Media upload URLs are permanent and can be used directly as
featured_imagevalues. - To find or regenerate your API key, go to Website Settings → Integrations in your dashboard at app.closedaily.com.
Error Codes
| Status | Meaning |
|---|---|
200 |
Success |
400 |
Bad request — missing required fields or invalid data |
401 |
Unauthorized — missing or invalid API key |
404 |
Not found — the post or category does not exist |
500 |
Server error — try again or contact support |