# Medium Scraper – Articles, Authors, Tags & Publications (`leadsbrary/medium-article-scraper`) Actor

Medium scraper: extract public articles by author, publication, tag or URL with title, full text, markdown, tags, claps, responses, dates and reading time. Export JSON, CSV or Excel. From $1 per 1,000 results.

- **URL**: https://apify.com/leadsbrary/medium-article-scraper.md
- **Developed by:** [Alexandre Manguis](https://apify.com/leadsbrary) (community)
- **Categories:** News, SEO tools, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 results

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Medium Scraper – Articles, Authors, Tags & Publications

### What does it do?

This Medium scraper collects **public Medium articles** by author, publication, tag or direct article URL. For every article it returns the title, subtitle, author, publication, tags, publication and update dates, reading time, claps, responses and — optionally — the full text as plain text and Markdown. Results are stored in an Apify dataset that you can export as JSON, CSV, Excel, XML or HTML, or read through the API.

It uses Medium's own public web endpoints, needs no Medium account and no API key, and works with Apify datacenter proxies. It only returns what is publicly visible: no emails, no private data, and no bypass of the Medium paywall.

### Who is it for / use cases

- **Content & SEO teams** – monitor what competitors and publications publish, track topics and tags.
- **Researchers and data teams** – build text corpora of articles on a topic (e.g. `machine-learning`) with dates, tags and engagement.
- **Newsletter and media curators** – pull the newest articles of selected authors or publications.
- **AI / RAG builders** – feed clean Markdown articles into pipelines.
- **Creators** – export your own articles with claps and responses for analysis or backup.

### Features

- Four source types in one run: authors, publications, tags, article URLs (mix freely, up to 500 sources).
- Full text as plain text **and** Markdown, plus word count (can be switched off for faster metadata-only runs).
- Engagement fields: claps and responses.
- Filters: total limit, per-source limit, `publishedAfter` date.
- Deduplication of articles across sources.
- Member-only stories: only the public preview is returned and flagged with `isMemberOnly` and `textIsPreviewOnly`.
- Every row carries `sourceType`, `sourceInput` and `scrapedAt`, so you know where it came from.

### Input

| Field | Type | Description |
|---|---|---|
| `authors` | array | `@username`, `username` or profile URL (`https://medium.com/@username`). |
| `publications` | array | Publication slug or URL (e.g. `better-programming`). |
| `tags` | array | Tag slug or URL (e.g. `machine-learning`). Newest first. |
| `articleUrls` | array | Direct article URLs. |
| `maxResults` | integer | Total limit, default 50 (max 100,000). Also caps your spending. |
| `maxResultsPerSource` | integer | Limit per author/publication/tag. Empty = only the total limit applies. |
| `includeFullText` | boolean | Fetch text, Markdown and word count. Default `true`. |
| `publishedAfter` | string | Only articles first published on or after `YYYY-MM-DD`. |
| `proxyConfiguration` | object | Apify datacenter proxy is enough. |

At least one author, publication, tag or article URL is required; otherwise the run fails with a clear "Invalid input" message.

Example input:

```json
{
  "authors": ["@zackbloom"],
  "publications": ["better-programming"],
  "tags": ["machine-learning"],
  "articleUrls": ["https://medium.com/@zackbloom/why-a-pneumatic-ventilator-e324da4a9b0f"],
  "maxResults": 45,
  "maxResultsPerSource": 8,
  "includeFullText": true,
  "proxyConfiguration": { "useApifyProxy": true }
}
```

### Output

One dataset row per article (text shortened below). Real sample from a production run:

```json
{
  "articleId": "62edea66ffa4",
  "url": "https://medium.com/blog/updating-our-rules-june-2023-62edea66ffa4",
  "canonicalUrl": "https://medium.com/blog/updating-our-rules-june-2023-62edea66ffa4",
  "title": "Updating Our Rules — June 2023",
  "subtitle": "This month, we are updating our Rules. As the world evolves, so do the ways in which people use Medium…",
  "authorName": "Medium",
  "authorUsername": "Medium",
  "authorUrl": "https://medium.com/@Medium",
  "publicationName": "The Medium Blog",
  "publicationSlug": "blog",
  "publicationDomain": null,
  "publishedAt": "2023-06-28T18:01:22.039Z",
  "updatedAt": "2023-06-28T18:01:22.039Z",
  "readingTimeMinutes": 1,
  "claps": 5130,
  "responses": 69,
  "tags": ["medium", "rules"],
  "isMemberOnly": false,
  "imageUrl": null,
  "text": "Updating Our Rules — June 2023\n\nThis month, we are updating our Rules…",
  "markdown": "## Updating Our Rules — June 2023\n\nThis month, we are updating our Rules…",
  "wordCount": 264,
  "textIsPreviewOnly": false,
  "sourceType": "author",
  "sourceInput": "@medium",
  "scrapedAt": "2026-10-03T22:32:31.485Z"
}
```

#### Output fields & fill rates

Measured on a production run of 41 articles (mixed authors, publications and tags, full text on):

| Field | Fill rate | Note |
|---|---|---|
| `articleId`, `url`, `canonicalUrl`, `title`, `publishedAt`, `updatedAt` | 100% | |
| `authorName`, `authorUsername`, `authorUrl` | 100% | |
| `claps`, `responses`, `readingTimeMinutes` | 100% | |
| `text`, `markdown`, `wordCount` | 100% | Preview only for member-only stories |
| `isMemberOnly`, `textIsPreviewOnly` | 100% | |
| `sourceType`, `sourceInput`, `scrapedAt` | 100% | |
| `subtitle`, `tags` | 92.7% | Some articles have none |
| `imageUrl` | 70.7% | Not every article has a cover image |
| `publicationName`, `publicationSlug` | 29.3% | Only for articles published in a publication |
| `publicationDomain` | 22% | Only for publications on a custom domain |

Empty fields mean the article does not have that data, not that extraction failed.

### How to use via API

Replace `YOUR_APIFY_TOKEN` with your token and use the Actor ID shown on this page (`<username>~medium-article-scraper`).

**curl**

```bash
curl -X POST "https://api.apify.com/v2/acts/<username>~medium-article-scraper/run-sync-get-dataset-items?token=YOUR_APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"tags": ["machine-learning"], "maxResults": 20}'
```

**JavaScript**

```javascript
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: 'YOUR_APIFY_TOKEN' });
const run = await client.actor('<username>/medium-article-scraper').call({
    authors: ['@medium'],
    maxResults: 20,
    includeFullText: true,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.length, items[0].title);
```

**Python**

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("<username>/medium-article-scraper").call(run_input={
    "publications": ["better-programming"],
    "maxResults": 20,
    "includeFullText": True,
})
items = client.dataset(run["defaultDatasetId"]).list_items().items
print(len(items), items[0]["title"])
```

### Pricing

Pay per result: you are charged for each article delivered in the dataset. The price per 1,000 results depends on your Apify plan:

| Plan | Price per 1,000 results |
|---|---|
| Free | $1.50 |
| Bronze | $1.30 |
| Silver | $1.15 |
| Gold and above | $1.00 |

Platform usage (compute and proxy) is billed by Apify on top; in the production test, a 41-article run used about $0.0007 of platform usage. Use `maxResults` to cap spending.

### Troubleshooting

- **"Invalid input: Provide at least one author, publication, tag or article URL."** – fill at least one of the four source fields.
- **No articles for a source** – check the spelling of the username, slug or tag; the log shows a warning per source with no results.
- **Short text and `textIsPreviewOnly: true`** – the story is member-only; only the public preview is available.
- **Fewer rows than `maxResults`** – the source has fewer articles, duplicates across sources are removed, or `publishedAfter` filtered them out.
- **Slow run** – turn off `includeFullText` for metadata-only extraction.

### FAQ

**Do I need a Medium account or API key?** No. Only public pages are read.

**Does it return emails or private data?** No. Only publicly displayed article data and public author names/usernames.

**Can it read paywalled stories?** No. Member-only stories return the public preview only, flagged in the output.

**Can I export to CSV or Excel?** Yes, from the dataset view or via the API (JSON, CSV, Excel, XML, HTML).

**Is scraping Medium allowed?** The Actor collects publicly visible data only. You are responsible for using it in line with Medium's Terms of Service, copyright law and applicable data-protection rules such as GDPR.

### Keywords

medium scraper, medium article scraper, medium publication scraper, scrape medium articles, medium author articles, medium tag scraper, medium blog scraper, medium posts export, medium to csv, medium content extractor, medium api alternative

### Hashtags

\#medium #mediumscraper #webscraping #articles #blogscraper #contentresearch #dataextraction #apify #json #csv

# Changelog

This Actor's version history is a separate document: https://apify.com/leadsbrary/medium-article-scraper/changelog.md

# Actor input Schema

## `authors` (type: `array`):

Medium authors: @username, username or profile URL (https://medium.com/@username). Their latest articles are scraped.

## `publications` (type: `array`):

Publication slug or URL (e.g. better-programming or https://medium.com/the-startup).

## `tags` (type: `array`):

Tag slugs or tag URLs (e.g. machine-learning or https://medium.com/tag/python). Newest articles first.

## `articleUrls` (type: `array`):

Direct article URLs (the 10–12 character id at the end of the URL is used).

## `maxResults` (type: `integer`):

Stop after this many articles in total (also protects your spending).

## `maxResultsPerSource` (type: `integer`):

Cap for each author, publication or tag. Empty = only the total limit applies.

## `includeFullText` (type: `boolean`):

Fetch each article's public text (text, markdown, word count). Member-only articles only expose the public preview, flagged with isMemberOnly/textIsPreviewOnly. Disable for faster metadata-only runs.

## `publishedAfter` (type: `string`):

Only articles first published on or after this date (YYYY-MM-DD).

## `proxyConfiguration` (type: `object`):

Datacenter proxy is enough for Medium.

## Actor input object example

```json
{
  "authors": [
    "@medium"
  ],
  "publications": [
    "better-programming"
  ],
  "tags": [
    "machine-learning"
  ],
  "articleUrls": [],
  "maxResults": 10,
  "includeFullText": true,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `results` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "authors": [
        "@medium"
    ],
    "publications": [
        "better-programming"
    ],
    "tags": [
        "machine-learning"
    ],
    "articleUrls": [],
    "maxResults": 10,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("leadsbrary/medium-article-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "authors": ["@medium"],
    "publications": ["better-programming"],
    "tags": ["machine-learning"],
    "articleUrls": [],
    "maxResults": 10,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("leadsbrary/medium-article-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "authors": [
    "@medium"
  ],
  "publications": [
    "better-programming"
  ],
  "tags": [
    "machine-learning"
  ],
  "articleUrls": [],
  "maxResults": 10,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call leadsbrary/medium-article-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,leadsbrary/medium-article-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/Cih3FliUf0sbCqZ6X/builds/nZIJQ12mAOe6pSTBn/openapi.json
