# Medium Article Discovery & Metadata Extractor (`dataspryva/medium-article-discovery-and-metadata-extractor`) Actor

Find Medium articles by tag, author, publication, keyword search or direct URL, returning title, author, tags, preview text, reading time, publish date and paywall status for each.

- **URL**: https://apify.com/dataspryva/medium-article-discovery-and-metadata-extractor.md
- **Developed by:** [Dataspryva](https://apify.com/dataspryva) (community)
- **Categories:** Automation, News, Lead generation
- **Features:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.49 / 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 Article Discovery & Metadata Extractor

Medium is full of useful writing, but browsing it by hand makes it hard to compare coverage across topics, authors and publications. This actor turns five common discovery paths — tag browsing, author feeds, publication feeds, keyword search and direct article URLs — into one consistent article dataset you can sort, filter and join with your own tools.

Every record describes a single article: who wrote it, where it was published, when it went live, how long it takes to read, how much engagement it has, whether it sits behind Medium's member paywall, and which topics and tags it carries.

### Discovery modes

| Mode | What it collects | Best for |
|---|---|---|
| `byTag` | Articles tagged with a topic, newest first or by top stories from the week/month/year | Trend tracking and editorial research |
| `byUser` | An author's articles, including posts distributed to publications | Monitoring a writer's output |
| `byPublication` | Articles published under a publication | Following a publication's latest work |
| `search` | Articles matching a keyword query | Broad topic discovery |
| `byUrl` | Metadata for one or more article URLs | Enriching a list of links you already have |

Tag, author, publication and search modes walk the source's pagination until `maxItems` is reached or the source runs out, so a run can return thousands of records rather than a single page.

### Output fields

| Field | Description |
|---|---|
| `articleId` | Medium's short article identifier |
| `title` | Article headline |
| `subtitle` | Article subtitle or standfirst, when present |
| `previewText` | Subtitle or search-style preview text, capped at 300 characters |
| `authorName` | Author display name |
| `authorUsername` | Medium handle without `@` |
| `authorBio` | Author bio, when the profile has one |
| `authorImageUrl` | Author avatar URL |
| `authorUrl` | Author profile URL |
| `publicationName` | Publication name, when the article belongs to one |
| `publicationSlug` | Publication slug |
| `publicationDomain` | Custom publication domain, when one is used |
| `publicationUrl` | Publication home URL |
| `publishedDate` | First publish time, ISO-8601 UTC |
| `updatedDate` | Latest update time, when it differs from `publishedDate` |
| `tags` | Article tag names |
| `topics` | Medium topic names attached to the article |
| `readingTimeMinutes` | Estimated reading time in whole minutes |
| `wordCount` | Article word count |
| `language` | Detected language code |
| `clapCount` | Clap count reported by Medium |
| `responseCount` | Number of responses on the article |
| `isPaywalled` | `true` when the article is member-only |
| `isSeries` | Present and `true` when the article belongs to a series |
| `articleUrl` | Canonical article URL from Medium |
| `canonicalUrl` | Canonical URL when it differs from `articleUrl` |
| `imageUrl` | Article preview image URL |
| `recordType` | Always `article` |
| `siteName` | Always `Medium` |
| `scrapedAt` | When the record was collected, ISO-8601 UTC |

Empty fields are omitted rather than written as `null`. A field appears only when Medium exposes that value for the article.

### Input fields

| Field | Type | Default | Used by |
|---|---|---|---|
| `mode` | select | `byTag` | All modes |
| `tag` | select | `programming` | `byTag` |
| `customTag` | text | – | `byTag` (overrides `tag`) |
| `sortBy` | select | `new` | `byTag` |
| `username` | text | – | `byUser` |
| `publication` | text | – | `byPublication` |
| `query` | text | `artificial intelligence` | `search` |
| `articleUrl` | text | – | `byUrl` |
| `fromDate` | text | – | All modes |
| `containsKeyword` | text | – | All modes |
| `excludePaywalled` | boolean | `false` | All modes |
| `maxItems` | integer | `5` | All modes |

#### Example: newest Python articles

```json
{
  "mode": "byTag",
  "tag": "python",
  "sortBy": "new",
  "maxItems": 25
}
```

#### Example: an author's recent work

```json
{
  "mode": "byUser",
  "username": "towardsdatascience",
  "maxItems": 50
}
```

#### Example: a publication's archive

```json
{
  "mode": "byPublication",
  "publication": "better-programming",
  "maxItems": 100
}
```

#### Example: keyword search with a date cut-off

```json
{
  "mode": "search",
  "query": "large language models",
  "fromDate": "2025-01-01",
  "excludePaywalled": true,
  "maxItems": 40
}
```

#### Example: enrich links you already have

```json
{
  "mode": "byUrl",
  "articleUrl": "https://medium.com/@author/example-article-abc123def456\nhttps://medium.com/p/abcdef123456",
  "maxItems": 10
}
```

### What you can do with it

- Build a topic monitoring sheet that shows which authors and publications are covering a subject each week.
- Compare top and newest stories for a tag to separate evergreen hits from fresh coverage.
- Filter out member-only posts before sending articles to a newsletter or internal digest.
- Enrich a list of saved links with author, publication, date, engagement and topic metadata.
- Track an author's or publication's output over time with scheduled runs.

### FAQ

**Do I need a Medium account or cookies?** No. The actor reads publicly available article metadata without logging in.

**Do I need to configure a proxy?** No. The actor picks a working connection automatically, so you never supply proxy settings, cookies or an API key.

**Can I get the full article text?** This actor focuses on discovery and metadata. It returns titles, previews, author and publication details and engagement metrics, not the article body.

**How far back can it go?** It follows the source's own pagination until `maxItems` is reached or the source runs out. Very large requests can span many pages, so raise `maxItems` deliberately.

**Why are some fields missing on some records?** Medium does not expose every field for every article. A personal post may have no publication, an older post may have no subtitle, and some articles have no tags. Missing values are omitted instead of filled with placeholders.

**How are paywalled articles handled?** `isPaywalled` marks member-only articles, and `excludePaywalled` removes them. Only public metadata is collected; the actor never tries to read member-only body content.

**What counts as an article URL?** Standard Medium links, `medium.com/p/...` short links, and articles hosted on a custom Medium domain such as a publication's own website.

**Can I combine filters?** Yes. `fromDate`, `containsKeyword` and `excludePaywalled` work together in every mode.

**Is this affiliated with Medium?** No. This is an independent third-party actor that reads publicly available article metadata. It is not operated by or affiliated with Medium.

# Actor input Schema

## `mode` (type: `string`):

Choose how to find articles: browse a tag, list an author's posts, list a publication's posts, search by keyword, or look up article URLs.

## `tag` (type: `string`):

Tag to browse. Pick a common topic or use the matching value in the dropdown; the latest or most-clapped stories for that tag are collected.

## `customTag` (type: `string`):

Use any Medium tag slug that is not in the list above. When set, it overrides the Tag selection.

## `sortBy` (type: `string`):

Order for tag browsing: newest first, or the most-clapped stories from the past week, month, or year.

## `username` (type: `string`):

Medium username (for example `towardsdatascience`) or a profile URL such as `https://medium.com/@towardsdatascience`. Used by the author mode.

## `publication` (type: `string`):

Publication slug as it appears in the URL (for example `towards-data-science`) or a full publication URL. Used by the publication mode.

## `query` (type: `string`):

Keywords to search for across Medium articles. Used by the search mode.

## `articleUrl` (type: `string`):

One or more article URLs, separated by new lines or commas. Medium URLs, custom-domain Medium articles, and `medium.com/p/...` links are accepted.

## `fromDate` (type: `string`):

Only keep articles published on or after this ISO date (for example `2025-01-01`).

## `containsKeyword` (type: `string`):

Only keep articles whose title or preview text contains this keyword (case-insensitive).

## `excludePaywalled` (type: `boolean`):

When enabled, member-only (paywalled) articles are skipped.

## `maxItems` (type: `integer`):

Maximum number of article records to save. Pagination stops when this number is reached or the source runs out.

## Actor input object example

```json
{
  "mode": "byTag",
  "tag": "programming",
  "sortBy": "new",
  "query": "artificial intelligence",
  "excludePaywalled": false,
  "maxItems": 5
}
```

# Actor output Schema

## `records` (type: `string`):

Dataset containing all scraped Medium article records.

# 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 = {
    "mode": "byTag",
    "tag": "programming",
    "sortBy": "new",
    "query": "artificial intelligence",
    "excludePaywalled": false,
    "maxItems": 5
};

// Run the Actor and wait for it to finish
const run = await client.actor("dataspryva/medium-article-discovery-and-metadata-extractor").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 = {
    "mode": "byTag",
    "tag": "programming",
    "sortBy": "new",
    "query": "artificial intelligence",
    "excludePaywalled": False,
    "maxItems": 5,
}

# Run the Actor and wait for it to finish
run = client.actor("dataspryva/medium-article-discovery-and-metadata-extractor").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 '{
  "mode": "byTag",
  "tag": "programming",
  "sortBy": "new",
  "query": "artificial intelligence",
  "excludePaywalled": false,
  "maxItems": 5
}' |
apify call dataspryva/medium-article-discovery-and-metadata-extractor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,dataspryva/medium-article-discovery-and-metadata-extractor"
        }
    }
}
```

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/zZrN5Pn5v9nkvhXVm/builds/Jr1uUwpcGS8evBFw1/openapi.json
