REST API reference

Medium API

13 endpoints for Medium. Each one is a POST with a JSON body, called with your API key.

Base URL
https://cotlet.dev/api/v1/medium
Connects with
Your own Medium account (writer or editor role in a publication to submit or schedule there)
Integration page
Medium
Medium Endpoints
Endpoint Access Description
list_stories read Your stories, newest first, with status, publication, tags and URL.
get_story read Full story: title, subtitle, body as Markdown, tags, canonical link, paywall and dates.
get_story_stats read Views, reads, member read ratio, claps, responses and earnings for paywalled stories.
get_account_stats read The account-level figures Medium shows on your stats page, for one month.
list_responses read Responses to a story, with author, text and whether you replied.
list_publications read Publications you own, edit or write for, with your role.
create_draft write New draft from Markdown. Nothing is public until you publish or schedule it.
import_story write Medium's "Import a story": copies a post from a URL into a draft, backdated, with a canonical link.
update_story write Change the title, subtitle, body, tags, canonical link or paywall of a draft or a published story.
publish_story write Publish a draft now.
schedule_story write Publish a draft at a set time.
submit_to_publication write Send a draft to the editors of a publication you write for.
reply_to_response write Reply as you under a response to your story.

POST https://cotlet.dev/api/v1/medium/list_stories

Lists your own stories, newest first. Filter by status to find drafts or scheduled stories. Each item has the story_id used by the other tools. Pages hold up to 50 stories; pass next_cursor back as cursor for the next page.

Parameters

Parameters: list_stories
NameTypeDescription
status enum published, draft or scheduled. Default: published.
limit integer 1 to 50. Default: 20.
cursor string Next page cursor from the previous call.

Request

cURL
curl -X POST https://cotlet.dev/api/v1/medium/list_stories \
  -H "Authorization: Bearer $COTLET_API_KEY"
Python
result = client.call("medium", "list_stories")
Node.js
const result = await cotlet.call('medium', 'list_stories');
CLI
cotlet call medium list_stories

Example response

{
  "stories": [
    {
      "story_id": "3f9a1c27b84e",
      "title": "Shipping small",
      "status": "published",
      "publication": null,
      "tags": [
        "Python",
        "Programming"
      ],
      "published_at": "2026-10-02T09:00:00-04:00",
      "url": "https://medium.com/@yourname/shipping-small-3f9a1c27b84e"
    }
  ],
  "next_cursor": null
}

Get story

read

POST https://cotlet.dev/api/v1/medium/get_story

Returns one story with its body as Markdown. Use it before update_story so your agent changes only the part you asked for. Works for drafts, scheduled and published stories.

Parameters

Parameters: get_story
NameTypeDescription
story_id required string Story ID from list_stories.

Request

cURL
curl -X POST https://cotlet.dev/api/v1/medium/get_story \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"story_id":"story_8c41d2"}'
Python
result = client.call("medium", "get_story", {"story_id":"story_8c41d2"})
Node.js
const result = await cotlet.call('medium', 'get_story', {"story_id":"story_8c41d2"});
CLI
cotlet call medium get_story --data '{"story_id":"story_8c41d2"}'

Example response

{
  "story_id": "3f9a1c27b84e",
  "title": "Shipping small",
  "subtitle": "Why small releases win",
  "body_markdown": "Small releases beat big ones.\n\nHere is why.",
  "tags": [
    "Python",
    "Programming"
  ],
  "canonical_url": "https://yourname.dev/blog/shipping-small",
  "paywalled": false,
  "status": "published"
}

POST https://cotlet.dev/api/v1/medium/get_story_stats

Returns the numbers from the story's detailed stats page. Member read ratio is member reads divided by member views, the figure Medium uses to adjust earnings. Earnings appear only for stories behind the paywall.

Parameters

Parameters: get_story_stats
NameTypeDescription
story_id required string Story ID from list_stories.

Request

cURL
curl -X POST https://cotlet.dev/api/v1/medium/get_story_stats \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"story_id":"story_8c41d2"}'
Python
result = client.call("medium", "get_story_stats", {"story_id":"story_8c41d2"})
Node.js
const result = await cotlet.call('medium', 'get_story_stats', {"story_id":"story_8c41d2"});
CLI
cotlet call medium get_story_stats --data '{"story_id":"story_8c41d2"}'

Example response

{
  "story_id": "3f9a1c27b84e",
  "views": 1840,
  "reads": 912,
  "member_read_ratio": 0.42,
  "claps": 143,
  "responses": 6,
  "earnings": {
    "amount": 18.4,
    "currency": "USD"
  }
}

POST https://cotlet.dev/api/v1/medium/get_account_stats

Returns the account-level figures Medium shows on your stats page for one month. Use list_stories and get_story_stats to find which stories drove them.

Parameters

Parameters: get_account_stats
NameTypeDescription
month string YYYY-MM. Default: current month.

Request

cURL
curl -X POST https://cotlet.dev/api/v1/medium/get_account_stats \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"month":"2026-09"}'
Python
result = client.call("medium", "get_account_stats", {"month":"2026-09"})
Node.js
const result = await cotlet.call('medium', 'get_account_stats', {"month":"2026-09"});
CLI
cotlet call medium get_account_stats --data '{"month":"2026-09"}'

Example response

{
  "month": "2026-09",
  "views": 9420,
  "reads": 4310
}

POST https://cotlet.dev/api/v1/medium/list_responses

Lists the responses under one story, with the author, text and whether you already replied. Hidden responses are not returned. Pass response_id to reply_to_response.

Parameters

Parameters: list_responses
NameTypeDescription
story_id required string Story ID from list_stories.
unanswered_only boolean Skip responses you already replied to.
cursor string Next page cursor from the previous call.

Request

cURL
curl -X POST https://cotlet.dev/api/v1/medium/list_responses \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"story_id":"story_8c41d2"}'
Python
result = client.call("medium", "list_responses", {"story_id":"story_8c41d2"})
Node.js
const result = await cotlet.call('medium', 'list_responses', {"story_id":"story_8c41d2"});
CLI
cotlet call medium list_responses --data '{"story_id":"story_8c41d2"}'

Example response

{
  "responses": [
    {
      "response_id": "r_41b9c2",
      "author": "Dana Whitfield",
      "body": "The third example has a typo.",
      "created_at": "2026-10-03T11:20:00Z",
      "replied": false
    }
  ],
  "next_cursor": null
}

POST https://cotlet.dev/api/v1/medium/list_publications

Returns every publication you belong to with your role. Use the publication_id with submit_to_publication. Each publication sets its own rules for what it accepts.

Parameters

No parameters.

Request

cURL
curl -X POST https://cotlet.dev/api/v1/medium/list_publications \
  -H "Authorization: Bearer $COTLET_API_KEY"
Python
result = client.call("medium", "list_publications")
Node.js
const result = await cotlet.call('medium', 'list_publications');
CLI
cotlet call medium list_publications

Example response

{
  "publications": [
    {
      "publication_id": "pub_7d21",
      "name": "Better Programming",
      "role": "writer"
    }
  ]
}

Create draft

write

POST https://cotlet.dev/api/v1/medium/create_draft

Creates a draft from Markdown and returns its story_id. The draft is private. It reaches no one and is deleted or edited in one call, so it asks for no confirmation.

Parameters

Parameters: create_draft
NameTypeDescription
title required string Story title.
body_markdown required string Story body. Images by URL.
subtitle string Shown under the title and in previews.
tags string[] Up to 5 tags.
canonical_url string Original URL if the story was first published elsewhere.
paywalled boolean Member-only story (Partner Program). Default: false.

Request

cURL
curl -X POST https://cotlet.dev/api/v1/medium/create_draft \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"title":"Shipping small","subtitle":"Why small releases win","body_markdown":"Small releases beat big ones.\n\n## Here is why","tags":["Python","Programming","Software Engineering"],"paywalled":true}'
Python
result = client.call("medium", "create_draft", {"title":"Shipping small","subtitle":"Why small releases win","body_markdown":"Small releases beat big ones.\n\n## Here is why","tags":["Python","Programming","Software Engineering"],"paywalled":True})
Node.js
const result = await cotlet.call('medium', 'create_draft', {"title":"Shipping small","subtitle":"Why small releases win","body_markdown":"Small releases beat big ones.\n\n## Here is why","tags":["Python","Programming","Software Engineering"],"paywalled":true});
CLI
cotlet call medium create_draft --data '{"title":"Shipping small","subtitle":"Why small releases win","body_markdown":"Small releases beat big ones.\n\n## Here is why","tags":["Python","Programming","Software Engineering"],"paywalled":true}'

Example response

{
  "story_id": "3f9a1c27b84e",
  "status": "draft",
  "url": "https://medium.com/p/3f9a1c27b84e/edit"
}

Import story

write

POST https://cotlet.dev/api/v1/medium/import_story

Runs Medium's importer on a public URL. The draft keeps the original date and gets a canonical link to the source. If Medium cannot read the page, the call fails and create_draft is the fallback. The draft is private, so no confirmation.

Parameters

Parameters: import_story
NameTypeDescription
url required string Public URL of the original post.

Request

cURL
curl -X POST https://cotlet.dev/api/v1/medium/import_story \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://yourname.dev/blog/shipping-small"}'
Python
result = client.call("medium", "import_story", {"url":"https://yourname.dev/blog/shipping-small"})
Node.js
const result = await cotlet.call('medium', 'import_story', {"url":"https://yourname.dev/blog/shipping-small"});
CLI
cotlet call medium import_story --data '{"url":"https://yourname.dev/blog/shipping-small"}'

Example response

{
  "story_id": "3f9a1c27b84e",
  "status": "draft",
  "canonical_url": "https://yourname.dev/blog/shipping-small"
}

Update story

write Confirm

POST https://cotlet.dev/api/v1/medium/update_story

Changes only the fields you send. On a published story the change is visible to readers at once. By default you confirm each call; you can switch that off for this tool.

Parameters

Parameters: update_story
NameTypeDescription
story_id required string Story ID from list_stories.
fields required object Any of title, subtitle, body_markdown, tags, canonical_url, paywalled.

Request

cURL
curl -X POST https://cotlet.dev/api/v1/medium/update_story \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"story_id":"3f9a1c27b84e","fields":{"body_markdown":"Small releases beat big ones.\n\nMore on [yourname.dev](https://yourname.dev)."}}'
Python
result = client.call("medium", "update_story", {"story_id":"3f9a1c27b84e","fields":{"body_markdown":"Small releases beat big ones.\n\nMore on [yourname.dev](https://yourname.dev)."}})
Node.js
const result = await cotlet.call('medium', 'update_story', {"story_id":"3f9a1c27b84e","fields":{"body_markdown":"Small releases beat big ones.\n\nMore on [yourname.dev](https://yourname.dev)."}});
CLI
cotlet call medium update_story --data '{"story_id":"3f9a1c27b84e","fields":{"body_markdown":"Small releases beat big ones.\n\nMore on [yourname.dev](https://yourname.dev)."}}'

Example response

{
  "story_id": "3f9a1c27b84e",
  "status": "published",
  "updated_fields": [
    "tags"
  ]
}

Publish story

write Confirm

POST https://cotlet.dev/api/v1/medium/publish_story

Publishes a draft under your name, or in the publication it belongs to. Readers and followers can see it at once. By default you confirm each call; you can switch that off for this tool.

Parameters

Parameters: publish_story
NameTypeDescription
story_id required string Story ID from list_stories.

Request

cURL
curl -X POST https://cotlet.dev/api/v1/medium/publish_story \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"story_id":"story_8c41d2"}'
Python
result = client.call("medium", "publish_story", {"story_id":"story_8c41d2"})
Node.js
const result = await cotlet.call('medium', 'publish_story', {"story_id":"story_8c41d2"});
CLI
cotlet call medium publish_story --data '{"story_id":"story_8c41d2"}'

Example response

{
  "story_id": "3f9a1c27b84e",
  "status": "published",
  "url": "https://medium.com/@yourname/shipping-small-3f9a1c27b84e"
}

Schedule story

write Confirm

POST https://cotlet.dev/api/v1/medium/schedule_story

Schedules a draft. Medium publishes it at the set time, and the story stays in your drafts until then. By default you confirm each call; you can switch that off for this tool.

Parameters

Parameters: schedule_story
NameTypeDescription
story_id required string Story ID from list_stories.
publish_at required datetime ISO 8601 with time zone.

Request

cURL
curl -X POST https://cotlet.dev/api/v1/medium/schedule_story \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"story_id":"3f9a1c27b84e","publish_at":"2026-10-15T09:00:00-04:00"}'
Python
result = client.call("medium", "schedule_story", {"story_id":"3f9a1c27b84e","publish_at":"2026-10-15T09:00:00-04:00"})
Node.js
const result = await cotlet.call('medium', 'schedule_story', {"story_id":"3f9a1c27b84e","publish_at":"2026-10-15T09:00:00-04:00"});
CLI
cotlet call medium schedule_story --data '{"story_id":"3f9a1c27b84e","publish_at":"2026-10-15T09:00:00-04:00"}'

Example response

{
  "story_id": "3f9a1c27b84e",
  "status": "scheduled",
  "publish_at": "2026-10-15T09:00:00-04:00"
}

Submit to publication

write Confirm

POST https://cotlet.dev/api/v1/medium/submit_to_publication

Submits a draft to a publication. Its editors review it and decide when to publish. A publication can accept only drafts, only published stories, or both. By default you confirm each call; you can switch that off for this tool.

Parameters

Parameters: submit_to_publication
NameTypeDescription
story_id required string Story ID from list_stories.
publication_id required string Publication ID from list_publications.

Request

cURL
curl -X POST https://cotlet.dev/api/v1/medium/submit_to_publication \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"story_id":"story_8c41d2","publication_id":"publication_8c41d2"}'
Python
result = client.call("medium", "submit_to_publication", {"story_id":"story_8c41d2","publication_id":"publication_8c41d2"})
Node.js
const result = await cotlet.call('medium', 'submit_to_publication', {"story_id":"story_8c41d2","publication_id":"publication_8c41d2"});
CLI
cotlet call medium submit_to_publication --data '{"story_id":"story_8c41d2","publication_id":"publication_8c41d2"}'

Example response

{
  "story_id": "3f9a1c27b84e",
  "publication_id": "pub_7d21",
  "status": "submitted"
}

Reply to response

write Confirm

POST https://cotlet.dev/api/v1/medium/reply_to_response

Posts a reply under one response, under your name. By default you confirm each call; you can switch that off for this tool.

Parameters

Parameters: reply_to_response
NameTypeDescription
response_id required string Response ID from list_responses.
body required string Reply text.

Request

cURL
curl -X POST https://cotlet.dev/api/v1/medium/reply_to_response \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"response_id":"response_8c41d2","body":"body"}'
Python
result = client.call("medium", "reply_to_response", {"response_id":"response_8c41d2","body":"body"})
Node.js
const result = await cotlet.call('medium', 'reply_to_response', {"response_id":"response_8c41d2","body":"body"});
CLI
cotlet call medium reply_to_response --data '{"response_id":"response_8c41d2","body":"body"}'

Example response

{
  "response_id": "r_41b9c2",
  "reply_id": "r_41ba07",
  "status": "published"
}