Blog API

Publish a post to the Fareletter blog with a single HTTP request, authenticated with a shared key (ask the site owner for one). Posts are not otherwise reviewed before going live. Submitted Markdown is still sanitized server-side (scripts, event handlers and dangerous links are stripped), so a mistaken or leaked-key post still can't attack other visitors.

Publish a post

POST https://fareletter.com/api/posts
Content-Type: application/json
Authorization: Bearer <your key>

FieldRequiredNotes
titleyes3-120 characters
markdownyes80-50,000 characters. Standard Markdown (GFM): headings (## and below), lists, links, images, blockquotes, code, tables. Raw HTML is stripped, not rejected - don't rely on embedding HTML
excerptnoUp to 300 characters; auto-derived from the post body if omitted
covernoA valid http(s) image URL, used as the card/social-share image. Images in the body are referenced by URL the same way - there is no upload endpoint
authornoUp to 80 characters; defaults to "Fareletter community"
tagsnoUp to 8 strings, 24 characters each

Request example

{
  "title": "My trip to Bangkok on a budget",
  "markdown": "## Why Bangkok\n\nBangkok is one of the cheapest long-haul-feeling trips from India...\n\n![Street food stall in Bangkok](https://example.com/photo.jpg)\n\n- Flights: look for AirAsia or IndiGo sales\n- Stay: Sukhumvit for easy transit\n\n> Book the non-stop if the price gap is under ₹2,000 - the connection risk isn't worth it for less.\n",
  "excerpt": "A short, practical guide to a budget Bangkok trip from India.",
  "cover": "https://example.com/cover.jpg",
  "author": "A reader",
  "tags": ["bangkok", "budget"]
}

Success response - 201 Created

{
  "ok": true,
  "slug": "my-trip-to-bangkok-on-a-budget",
  "url": "https://fareletter.com/blog/my-trip-to-bangkok-on-a-budget",
  "title": "My trip to Bangkok on a budget",
  "excerpt": "A short, practical guide to a budget Bangkok trip from India.",
  "date": "2026-10-24",
  "mins": 2
}

Errors

StatuserrorWhen
400validationA field fails the rules above - the response includes a fields object naming each problem
400invalid_jsonBody isn't valid JSON
401unauthorizedMissing or wrong Authorization: Bearer key
409slug_exhaustedExtremely unlikely - too many posts already share the derived slug
413payload_too_largeRequest body over ~80,000 bytes
415unsupported_media_typeContent-Type isn't application/json
429rate_limitedMore than 3 posts from the same IP in an hour

List posts

GET https://fareletter.com/api/posts returns the most recent posts as JSON:

{ "posts": [ { "slug": "...", "title": "...", "excerpt": "...", "cover": "...", "author": "...", "tags": [], "date": "2026-10-24", "mins": 2, "url": "..." } ] }

curl example

curl -X POST https://fareletter.com/api/posts \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_KEY" \
  -d '{"title":"My trip to Bangkok","markdown":"## Why Bangkok\n\nSome text about the trip...\n\n![Street food](https://example.com/photo.jpg)"}'

Limits

See the blog