Looking for the overview — what this API returns, what it costs, and a call you can run without a key? See the Medium API page →
Social Media

Medium API & Scraper

The Medium API returns author and article data as clean JSON.

20 actionsLive JSON1,000 free credits$0.67–$1.50 / 1,000 creditsMCP-ready
Get a free keyOpen in playground

🤖 Using an AI assistant? Copy this link into ChatGPT / Claude / Cursor — it reads every endpoint and parameter instantly and tells you if this API fits your use case.

The primary user endpoint returns a user's id, username, name, bio and follower, following and post counts, and you can resolve a user_id, pull user_articles, user_top_articles, an article, its content and its HTML. It is built for content research and author intelligence that need Medium data without a scraper. One ReefAPI key, one shared credit pool, the standard envelope.

Reference

Medium ids, timestamps and the paywall you cannot see

Medium has no paywall flag in this response, two different date formats depending on which action you call, and two different pagination cursor shapes. Measured 2026-08-27 against @dhh, the Signal v. Noise publication and the python and machine-learning tag feeds.

FieldWhat it holdsMeasured 2026-08-27
Article id12 lowercase hex characters, the tail of the URL slug41adf356857f, from .../reconsider-41adf356857f
user_id and collection_idThe same 12-hex shape as an article iddhh is 54bcbf647830; Signal v. Noise is 668e14b18fb1
published_at and created_atUnix epoch MILLISECONDS, not seconds1446644507775 is 2015-11-04
published_at from the tag actionAn RFC-822 date string instead, because that surface is the topic feed"Wed, 26 Aug 2026 22:02:12 GMT"
reading_timeFractional minutes, not a rounded integer11.599056603773585 on a 2,822-word post
word_count against the returned bodyword_count is Medium's full count; a member-only post returns only a free previewOne tagged post: word_count 1,338 against 160 body words. Five siblings returned 98 to 101%
next_cursor on user_articles and publication_articlesAn epoch-milliseconds string"1545257107680"
next_cursor on article_responsesoffset:id, a different shape entirely"45:ade9442ee53d"
collection_idNon-null means the post was published in a publication, and its URL sits under the publication pathRECONSIDER lives at /signal-v-noise/..., not /@dhh/...
Publication slugFollows renames, so the returned slug can differ from the one you sentslug "towards-data-science" came back as slug "data-science", name "TDS Archive"
Body formatsarticle_content, article_html and article_markdown return the same paragraphs in three encodingsSame post, 67 paragraphs: 16,272 chars plain, 16,689 HTML, 16,306 markdown

publication.created_at came back null on all three publications measured, and publication.url is inconsistent: a custom-domain publication returned "betterprogramming.pub" with no scheme while medium.com-hosted ones returned a full https URL. Normalize it before using it as a link.

Live example

Real request and response JSON

Captured from the indexed primary action, user, on .

Captured request
{
  "method": "POST",
  "url": "https://api.reefapi.com/medium/v1/user",
  "headers": {
    "x-api-key": "$REEF_KEY",
    "content-type": "application/json"
  },
  "body": {
    "user": "dhh"
  }
}
Captured response
{
  "ok": true,
  "meta": {
    "api": "medium",
    "endpoint": "user",
    "mode": "live",
    "latency_ms": 2111.4,
    "record_count": 1,
    "bytes": 38389,
    "cache_hit": false,
    "method": "format_json_profile"
  },
  "data": {
    "user": {
      "user_id": "54bcbf647830",
      "username": "dhh",
      "name": "DHH",
      "bio": "Creator of Ruby on Rails, Founder & CTO at Basecamp (formerly 37signals), NYT Best-selling author of REWORK and REMOTE, and Le Mans class-winning racing driver.",
      "created_at": 1391048788962,
      "twitter_screen_name": "dhh",
      "is_writer_program_enrolled": true,
      "posts_published_count": 191,
      "followers_count": 124738,
      "following_count": 100,
      "author_tags": [
        {
          "slug": "startup",
          "name": "Startup",
          "post_count": 495076
        },
        {
          "slug": "artificial-intelligence",
          "name": "[redacted-name]",
          "post_count": 489834
        },
        {
          "slug": "entrepreneurship",
          "name": "Entrepreneurship",
          "post_count": 401173
        }
      ],
      "publication_ids": [
        "668e14b18fb1"
      ]
    }
  }
}
Actions

What the Medium API does

ActionDescriptionConcrete use caseKey params
userPublic Medium user profile by username.Social-listening tools call user to get public Medium user profile by username..user
user_idResolve username to Medium userId.Creator and influencer platforms call user_id to resolve username to Medium userId..user
user_articlesAn author's published articles, newest first, with deep pagination.Brand-monitoring teams call user_articles to get an author's published articles, newest first, with deep pagination..user, limit, cursor
user_top_articlesUser articles sorted by clap count from profile references.Audience analysts call user_top_articles to get user articles sorted by clap count from profile references..user, limit
articleArticle metadata by url or id.Social-listening tools call article to get article metadata by url or id..url, id
article_contentArticle plain-text body from bodyModel paragraphs.Creator and influencer platforms call article_content to get article plain-text body from bodyModel paragraphs..url, id
article_htmlArticle HTML body from bodyModel paragraphs.Brand-monitoring teams call article_html to get article HTML body from bodyModel paragraphs..url, id
article_markdownArticle markdown body from bodyModel paragraphs.Audience analysts call article_markdown to get article markdown body from bodyModel paragraphs..url, id
article_responsesPublic responses/comments for an article.Social-listening tools call article_responses to get public responses/comments for an article..url, id, limit, cursor
publicationPublication/collection metadata by slug.Creator and influencer platforms call publication to get publication/collection metadata by slug..slug
publication_idResolve publication slug to collection id.Brand-monitoring teams call publication_id to resolve publication slug to collection id..slug
publication_articlesA publication's articles, newest first, with deep pagination.Audience analysts call publication_articles to get a publication's articles, newest first, with deep pagination..slug, limit, cursor
tagLatest posts for a topic tag (the tag/topic feed).Social-listening tools call tag to get latest posts for a topic tag (the tag/topic feed)..tag, limit
search_usersLook up an author by exact username. (Medium gates fuzzy people-search behind a logged-in JS app, so this resolves q as a username and returns that profile.)Creator and influencer platforms call search_users to look up an author by exact username.q
search_publicationsLook up a publication by exact slug. (Fuzzy publication search is login/JS-gated on Medium, so this resolves q as a publication slug and returns that publication.)Brand-monitoring teams call search_publications to look up a publication by exact slug.q
search_tagsCheck whether a topic tag exists and is active on Medium (resolves q as a tag slug).Audience analysts call search_tags to check whether a topic tag exists and is active on Medium (resolves q as a tag slug)..q
followersAn author's followers (paginated). Identify the author by 'user' or 'user_id'.Social-listening tools call followers to get an author's followers (paginated).user, user_id, limit, cursor
followingThe authors a user follows (paginated). Identify the user by 'user' or 'user_id'.Creator and influencer platforms call following to get the authors a user follows (paginated).user, user_id, limit, cursor
listReading list metadata and articles from public list page HTML. Provide the full list 'url', or 'id' together with 'user'.Brand-monitoring teams call list to get reading list metadata and articles from public list page HTML.id, url, user, username, slug, ...
search_articlesSearch Medium articles by keyword — returns matching articles with title, URL and publication details from medium.com.Audience analysts call search_articles to search Medium articles by keyword.q, limit, cursor
Code samples

Call user from your stack

curl -X POST https://api.reefapi.com/medium/v1/user \
  -H "x-api-key: $REEF_KEY" \
  -H "content-type: application/json" \
  -d '{"user":"dhh"}'
MCP one-liner
Ask your MCP-connected assistant: call reefapi.medium.user with {"user":"dhh"}.
Use cases

Who uses this API and why

  • Content research calls user_articles to study an author's output.
  • Author-intelligence uses user and user_top_articles to profile a writer.
  • RAG pipelines use article_content to ingest a post as clean text.
FAQ

Questions developers ask before integrating

What is a Medium post id and where do I find it?

It is the 12-character lowercase hex string at the end of every article URL. In https://medium.com/signal-v-noise/reconsider-41adf356857f the id is 41adf356857f. The article action takes either id or url, and on 2026-08-27 both returned identical objects. User ids and publication (collection) ids use the same 12-hex format, so a bare id on its own does not tell you what kind of object it points at.

Are Medium timestamps in seconds or milliseconds?

Milliseconds, on every action except one. created_at and published_at come back as epoch milliseconds, so 1446644507775 is 2015-11-04, and dividing by 1000 first is the usual fix for a date that lands in 1970. The exception is the tag action, which reads the topic feed and returns published_at as an RFC-822 string like "Wed, 26 Aug 2026 22:02:12 GMT". If you merge tag results with article results, normalize the dates first.

Is there a member-only or paywall flag on an article?

No. The article object has fourteen keys and none of them marks a post as member-only. What you can do is compare word_count against the body you actually got. On 2026-08-27, five of six posts pulled from the machine-learning feed returned 98 to 101% of their stated word_count, and the sixth returned 160 words against a word_count of 1,338. That twelve-percent ratio is the free preview of a locked post. paragraph_count in meta is a useful second signal: the truncated post had 9 paragraphs, the full ones had 54 and more.

Why is reading_time a decimal?

It is Medium's raw estimate in minutes and it is not rounded: a 2,822-word post returned 11.599056603773585 and a 564-word post returned 2.128301886792453. Round it yourself for display. The underlying arithmetic is roughly 265 words a minute plus image time, which is why it never lands on a whole number.

Why did user_articles return four articles when I asked for ten?

Because limit is an upper bound on one page of the profile stream, not a guarantee. Asking for 10 from @dhh returned 4 articles with has_more true and a next_cursor, even though that profile's posts_published_count is 191. Keep passing the previous response's next_cursor until has_more goes false. Ordering on page one is not strictly newest-first either: the first item returned was from 2015 and the second from 2019, because the profile's featured post leads the stream.

Does user_top_articles give me an author's all-time most-clapped posts?

No, and the difference matters. It sorts the same profile-stream page by clap count rather than asking Medium for a global ranking. Measured on @dhh, both actions returned the same four articles, with user_top_articles reordering them to 31,648 / 4,322 / 3,479 / 1,424 claps. For a genuine all-time top you need to page through user_articles with next_cursor and sort the full set yourself.

How do I tell an author's own post from one published in a publication?

Read collection_id. When it is null the post sits on the author's profile at medium.com/@handle. When it is set, the post was published in that publication and its URL uses the publication path: RECONSIDER carries creator_id 54bcbf647830 (dhh) and collection_id 668e14b18fb1, and lives at medium.com/signal-v-noise/reconsider-41adf356857f. The publication action turns a slug into that same id, so you can join the two.

What does a Medium response look like in the data?

A response is itself a post, not a comment record. Each one has its own 12-hex id, its own URL under the responder's handle, a clap count and a created_at in milliseconds. The title field is an auto-generated truncation of the opening line, so it often repeats the start of text. Responses page with a cursor shaped offset:id, for example "45:ade9442ee53d", which is a different format from the epoch cursor used by user_articles.

What is the Medium API?

Medium API is a ReefAPI endpoint group for medium It returns live JSON through POST requests under /medium/v1.

Is the Medium API free to try?

Yes. ReefAPI starts with 1,000 free credits, no card required. Medium calls use the same shared credit balance as every other ReefAPI engine.

Do I need a Medium login or account?

No login to Medium is needed for the API response. You call ReefAPI with your x-api-key header, and the playground can run live examples before you create a production key.

How fresh is the Medium data?

The page example is captured from a live user call, and production requests fetch live data through ReefAPI rather than a static sample.

How many credits does the Medium API use?

Medium actions currently cost 1 credit per successful call. Failed or blocked calls are free, and all APIs draw from one credit pool.

Can I call Medium from an AI assistant or MCP client?

Yes. Connect ReefAPI once through MCP and your assistant can call medium actions with the same key, credit pool and JSON envelope used by normal REST requests.

docs / medium

Medium

Medium

base /medium/v120 endpoints
post/medium/v1/user1 credit

Public Medium user profile by username.

ParameterAllowed / rangeDescription
userrequiredMedium username, with or without the leading @ (the name in medium.com/@<user>).
Try in playground →
post/medium/v1/user_id1 credit

Resolve username to Medium userId.

ParameterAllowed / rangeDescription
userrequiredMedium username, with or without the leading @ (the name in medium.com/@<user>).
Try in playground →
post/medium/v1/user_articles1 credit

An author's published articles, newest first, with deep pagination.

ParameterAllowed / rangeDescription
userrequiredMedium username, with or without the leading @ (the name in medium.com/@<user>).
limit = 10optional1–50Max items to return (1-50). Larger values are clamped to 50.
cursoroptionalPagination cursor from a previous response's next_cursor. Omit for page one.
Try in playground →
post/medium/v1/user_top_articles1 credit

User articles sorted by clap count from profile references.

ParameterAllowed / rangeDescription
userrequiredMedium username, with or without the leading @ (the name in medium.com/@<user>).
limit = 25optional1–50Max items to return (1-50). Larger values are clamped to 50.
Try in playground →
post/medium/v1/article1 credit

Article metadata by url or id.

ParameterAllowed / rangeDescription
urloptionalFull article URL (alternative to id).
idoptionalArticle post id — the 12-char hex at the end of an article URL. Provide id OR url.
Try in playground →
post/medium/v1/article_content1 credit

Article plain-text body from bodyModel paragraphs.

ParameterAllowed / rangeDescription
urloptionalFull article URL (alternative to id).
idoptionalArticle post id — the 12-char hex at the end of an article URL. Provide id OR url.
Try in playground →
post/medium/v1/article_html1 credit

Article HTML body from bodyModel paragraphs.

ParameterAllowed / rangeDescription
urloptionalFull article URL (alternative to id).
idoptionalArticle post id — the 12-char hex at the end of an article URL. Provide id OR url.
Try in playground →
post/medium/v1/article_markdown1 credit

Article markdown body from bodyModel paragraphs.

ParameterAllowed / rangeDescription
urloptionalFull article URL (alternative to id).
idoptionalArticle post id — the 12-char hex at the end of an article URL. Provide id OR url.
Try in playground →
post/medium/v1/article_responses1 credit

Public responses/comments for an article.

ParameterAllowed / rangeDescription
urloptionalFull article URL (alternative to id).
idoptionalArticle post id — the 12-char hex at the end of an article URL. Provide id OR url.
limit = 10optional1–50Max items to return (1-50). Larger values are clamped to 50.
cursoroptionalPagination cursor from a previous response's next_cursor. Omit for page one.
Try in playground →
post/medium/v1/publication1 credit

Publication/collection metadata by slug.

ParameterAllowed / rangeDescription
slugrequiredPublication slug — the path segment in medium.com/<slug> (or its custom-domain handle).
Try in playground →
post/medium/v1/publication_id1 credit

Resolve publication slug to collection id.

ParameterAllowed / rangeDescription
slugrequiredPublication slug — the path segment in medium.com/<slug> (or its custom-domain handle).
Try in playground →
post/medium/v1/publication_articles1 credit

A publication's articles, newest first, with deep pagination.

ParameterAllowed / rangeDescription
slugrequiredPublication slug — the path segment in medium.com/<slug> (or its custom-domain handle).
limit = 10optional1–50Max items to return (1-50). Larger values are clamped to 50.
cursoroptionalPagination cursor from a previous response's next_cursor. Omit for page one.
Try in playground →
post/medium/v1/tag1 credit

Latest posts for a topic tag (the tag/topic feed).

ParameterAllowed / rangeDescription
tagrequiredMedium topic tag slug (e.g. python, machine-learning).
limit = 10optional1–25Max items to return (1-25). Larger values are clamped to 25.
Try in playground →
post/medium/v1/search_users1 credit

Look up an author by exact username. (Medium gates fuzzy people-search behind a logged-in JS app, so this resolves q as a username and returns that profile.)

ParameterAllowed / rangeDescription
qrequiredSearch term.
Try in playground →
post/medium/v1/search_publications1 credit

Look up a publication by exact slug. (Fuzzy publication search is login/JS-gated on Medium, so this resolves q as a publication slug and returns that publication.)

ParameterAllowed / rangeDescription
qrequiredSearch term.
Try in playground →
post/medium/v1/search_tags1 credit

Check whether a topic tag exists and is active on Medium (resolves q as a tag slug).

ParameterAllowed / rangeDescription
qrequiredSearch term.
Try in playground →
post/medium/v1/followers1 credit

An author's followers (paginated). Identify the author by 'user' or 'user_id'.

ParameterAllowed / rangeDescription
useroptionalMedium username (with/without @). Provide user OR user_id.
user_idoptionalMedium numeric/hash user id (a profile's user_id). Provide user_id OR user.
limit = 10optional1–50Max items to return (1-50). Larger values are clamped to 50.
cursoroptionalPagination cursor from a previous response's next_cursor. Omit for page one.
Try in playground →
post/medium/v1/following1 credit

The authors a user follows (paginated). Identify the user by 'user' or 'user_id'.

ParameterAllowed / rangeDescription
useroptionalMedium username (with/without @). Provide user OR user_id.
user_idoptionalMedium numeric/hash user id (a profile's user_id). Provide user_id OR user.
limit = 10optional1–50Max items to return (1-50). Larger values are clamped to 50.
cursoroptionalPagination cursor from a previous response's next_cursor. Omit for page one.
Try in playground →
post/medium/v1/list1 credit

Reading list metadata and articles from public list page HTML. Provide the full list 'url', or 'id' together with 'user'.

ParameterAllowed / rangeDescription
idoptionalReading-list id (the trailing id in a list URL). Provide with 'user', or instead pass the full list 'url'.
urloptionalFull reading-list URL (alternative to user + id).
useroptionalList owner's username (with/without @; also accepted as 'username'). Combine with id, or pass the full url.
slugoptionalOptional list slug (the human-readable part of a list URL).
limit = 25optional1–50Max items to return (1-50). Larger values are clamped to 50.
Try in playground →
post/medium/v1/search_articles1 credit

Search Medium articles by keyword — returns matching articles with title, URL and publication details from medium.com.

ParameterAllowed / rangeDescription
qrequiredSearch term.
limit = 10optional1–50Max items to return (1-50). Larger values are clamped to 50.
cursoroptionalPagination cursor from a previous response's next_cursor. Omit for page one.
Try in playground →
Comparing scraping APIs?ReefAPI vs Apify