# X Trends Scraper by Country (`renzomacar/x-trends-scraper-by-country`) Actor

Pull the live X (Twitter) trending list for any of 62 countries and 400+ cities, with rank, search URL and the snapshot timestamp X itself reports. No login. Same price on every Apify plan.

- **URL**: https://apify.com/renzomacar/x-trends-scraper-by-country.md
- **Developed by:** [Renzo Madueno](https://apify.com/renzomacar) (community)
- **Categories:** Social media, News
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.20 / 1,000 trend returneds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## X Trends Scraper by Country

Pull the live X (Twitter) trending list for any country or city X ranks. Rank, topic name, the search URL, whether it is a hashtag, whether it is promoted, and X's own timestamps for when the list was built.

**467 locations available: 62 countries, 402 cities, plus worldwide. Up to 50 trends each.**

No login. No cookies. No browser. No Twitter account.

### What makes this one worth using

**Same price on every Apify plan.** $0.40 per 1,000 trends on the free plan and on every paid plan. No free-tier multiplier, no reduced result cap for free users.

**An empty location costs nothing.** If X publishes no trends for a place at the moment you ask, you get a `FAILURES` entry saying so — not a padded row and not a charge.

**A location X does not rank is an error, not an invoice.** Ask for "Atlantis" and the run tells you X ranks 62 countries and around 400 cities, and that yours is not one of them. It does not return an empty object and bill you for it.

**`tweetVolume` is reported as `null`, because that is what X returns.** Verified across Worldwide, the United States, Brazil, Mexico, Peru and India on 2026-08-22: X returns `null` for volume on every trend in every location. This actor passes that through untouched. It does not estimate, interpolate, or substitute a plausible-looking number. If you see a trending-topic scraper showing you tweet volumes, ask where the number came from.

### Input

```json
{
  "countries": ["worldwide", "United States", "PE", "Lima", "23424768"],
  "maxItemsPerTarget": 50
}
```

Locations can be given in whatever form you have them:

| You pass | It resolves to |
|---|---|
| `"worldwide"`, `"world"`, `"global"` | The global list |
| `"Peru"` | The country |
| `"PE"` | The country, by ISO code |
| `"Lima"` | The city, when `includeCities` is on |
| `"23424919"` | Directly by WOEID |
| `"all"` | Every one of the 62 countries |
| `"all cities"` | Every one of the 402 cities |

| Field | Type | Default | What it does |
|---|---|---|---|
| `countries` | array | `["worldwide"]` | Locations. Aliases: `country`, `places`, `place`, `locations`, `location`, `woeids`, `woeid`. |
| `maxItemsPerTarget` | integer | `50` | Per-location ceiling. X publishes at most 50. Aliases: `maxTrendsPerPlace`, `perTargetLimit`. |
| `maxItems` | integer | none | Ceiling across the whole run. Aliases: `maxResults`, `limit`, `maxTrends`. |
| `includeCities` | boolean | `false` | Let ambiguous names resolve to cities. Off by default, so "Mexico" gives you the country. |

#### On date filters

A trending list is a live snapshot. There is no per-trend timestamp to filter on, so **a date filter is rejected with an explicit error** rather than accepted and ignored:

> X Trends Scraper by Country cannot filter by date on this data (`startDate` was supplied). X does not expose a usable timestamp for this surface, so honouring the filter is impossible and ignoring it would hand you rows outside your range. This actor has no date filter.

What you get instead is honest freshness metadata on every row — see below. To build a time series, schedule the actor and let each run be one snapshot.

### Output

One row per trend.

| Field | Notes |
|---|---|
| `rank` | 1 to 50, in the order X returned them. |
| `name` | The trend as displayed, e.g. `#SmackDown` or `Nick Chubb`. |
| `isHashtag` | Whether the name starts with `#`. |
| `query` | The decoded search query behind the trend. |
| `searchUrl` | A working `https://x.com/search?q=...` link. |
| `isPromoted` | Whether it is a paid placement rather than an organic trend. |
| `tweetVolume` | **Always `null`.** X stopped publishing it. Reported honestly, never estimated. |
| `placeName`, `placeType`, `woeid` | The location, its type (`Country`, `Town`, `Supername`), and its WOEID. |
| `countryName`, `countryCode` | Filled for cities too, so city rows still group by country. |
| `trendsAsOf` | X's `as_of` timestamp for the response. |
| `listGeneratedAt` | X's `created_at` — when it actually built the list. |
| `scrapedAt` | When this actor fetched it. |

#### The freshness detail nobody mentions

`trendsAsOf` and `listGeneratedAt` are not the same thing, and the gap is often hours. Measured on 2026-08-22: the Worldwide list reported `as_of` at 04:02 UTC but `created_at` at 00:19 UTC the previous day — a gap of nearly 28 hours. The United States list showed 04:02 against 09:52 the day before.

`listGeneratedAt` is when X actually computed the ranking. If you are timestamping a trend for analysis, that is usually the field you want, and this actor passes both through so you can decide. The `freshness` dataset view puts them side by side with `scrapedAt`.

### How it works

Two endpoints, both of which x.com serves to logged-out clients:

1. `GET /1.1/trends/available.json` — X's full catalogue of ranked locations, fetched fresh at the start of every run rather than hardcoded, so the actor tracks locations X adds or drops.
2. `GET /1.1/trends/place.json?id=<woeid>` — the current list for one location.

Authentication is a guest token from `POST /1.1/guest/activate.json`: free, tied to no account, and refreshed automatically inside the run whenever X rotates or rejects it.

No browser, no proxy required, 512 MB of memory. Fetching all 62 countries takes under a minute.

### Errors go where you can act on them

Nothing that failed is written into the dataset. It goes into the `FAILURES` key-value record:

```json
{
  "actor": "X Trends Scraper by Country",
  "targetsRequested": 3,
  "resultsDelivered": 15,
  "resultsCharged": 15,
  "failureCount": 1,
  "failures": [
    {
      "target": "Atlantis",
      "error": "X does not publish a trend list for \"Atlantis\". It ranks trends for 62 countries and around 400 cities only."
    }
  ],
  "notes": [
    "tweetVolume is null on every row because X stops publishing it to logged-out clients. It is reported as null rather than estimated."
  ]
}
```

**A run that delivers nothing is marked FAILED**, not quietly succeeded over an empty dataset. You are charged nothing either way.

### Pricing

**$0.40 per 1,000 trends. Pay per result. No subscription. Same price on every Apify plan.**

| What you fetch | Trends | Cost |
|---|---|---|
| Worldwide only | 50 | $0.02 |
| One country | up to 50 | $0.02 |
| Ten countries | up to 500 | $0.20 |
| Every country (`"all"`) | up to 3,100 | $1.24 |
| Every country, hourly for a month | ~2.2M | ~$900 |
| Every country, every 6 hours for a month | ~372,000 | ~$149 |

Locations X does not rank, and locations with no trends right now, cost $0.00.

### Typical uses

- **Multi-market social listening.** Fetch all 62 countries in one run and see which topics cross borders and which stay local. A single run costs $1.24.
- **A trend time series.** Schedule the actor hourly against your markets and append to the same dataset. `listGeneratedAt` gives you an honest x-axis. This is the closest thing to a historical trends archive you can build without paying enterprise rates, and building it early is the whole point.
- **Content and newsroom timing.** Know what is trending in your target market before you publish, in the market's own language.
- **Ad-buying context.** `isPromoted` separates paid placements from organic ones, which tells you who is buying attention in a market right now.
- **Localisation research.** Compare the same brand or event across Spain, Mexico, Argentina and Peru to see how framing shifts.
- **Hashtag discovery.** Filter `isHashtag` and take the live hashtag set for a market straight from X's own ranking.

### Limits worth knowing before you start

- **62 countries, not 195.** X ranks trends only for the locations it chooses to. The list is fetched live at the start of each run, so it is always current, and anything outside it is reported as a clear failure rather than billed.
- **`tweetVolume` is always `null`.** Covered above. This is X's behaviour, not a gap in the actor.
- **The lists move on X's schedule, not yours.** `listGeneratedAt` can lag `trendsAsOf` by many hours. Polling faster than X regenerates gets you duplicate snapshots — which you would still be charged for, so use `listGeneratedAt` to deduplicate.
- **No historical data.** X publishes the current list only. History is something you accumulate by scheduling this actor, not something anyone can retrieve retroactively.
- **No keyword search.** This actor reports what X ranks. It does not search X, because X blocks the search timeline for guests entirely.

### Verified

Every endpoint, field, location count and null-volume claim in this README was tested live against X on **2026-08-22** from a datacenter IP with no cookies and no account. The 467 locations, the 62 countries and the `as_of` versus `created_at` gap are measurements from that day.

### Related actors

- **Tweet Scraper by URL or ID** — individual tweets with full metrics, $0.20/1k.
- **X Profile Scraper - No Login** — bulk handle-to-profile lookup, $0.25/1k.
- **X Profile Posts Scraper - No Cookies** — an account's timeline with a real per-account cap, $0.30/1k.
- **Tweet Replies Scraper - No Login** — publicly visible replies, $0.30/1k.

Same rules across all of them: one price on every plan, and nothing charged for nothing.

# Actor input Schema

## `countries` (type: `array`):

One location per line. Accepts a country name ("Peru"), an ISO country code ("PE"), a city X supports ("Lima"), a numeric WOEID (23424919), "worldwide", "all" for every country, or "all cities". X ranks trends for 62 countries and around 400 cities; anything outside that list is reported as a failure rather than billed as an empty result.

## `maxItemsPerTarget` (type: `integer`):

Per-location ceiling. X publishes up to 50, which is the default.

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

Ceiling across every location in the run. Leave empty for no overall cap. Useful when you pass "all", which resolves to 62 countries and up to 3,100 trends.

## `includeCities` (type: `boolean`):

Off by default. When off, an ambiguous name resolves to the country rather than the city of the same name. Turn it on to let city matches through.

## Actor input object example

```json
{
  "countries": [
    "worldwide"
  ],
  "maxItemsPerTarget": 50,
  "includeCities": false
}
```

# Actor output Schema

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

One row per trending topic: rank, name, search URL and the place it is trending in.

# 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 = {
    "countries": [
        "worldwide",
        "United States",
        "PE"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("renzomacar/x-trends-scraper-by-country").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 = { "countries": [
        "worldwide",
        "United States",
        "PE",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("renzomacar/x-trends-scraper-by-country").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 '{
  "countries": [
    "worldwide",
    "United States",
    "PE"
  ]
}' |
apify call renzomacar/x-trends-scraper-by-country --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,renzomacar/x-trends-scraper-by-country"
        }
    }
}

```

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/51LK5hi3kAHJZbRIe/builds/59kpJiDOCWTiqqLSo/openapi.json
