API Documentation - CloseDaily

API Documentation

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 a scheduled_at datetime (ISO 8601) to auto-publish at a future time. The scheduler checks every 60 seconds.
  • Read time is auto-calculated from content_html word 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_image values.
  • 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