# Wikimedia Commons Image Search: Free Images with Credit Lines (`yadroo/wikimedia-commons-images`) Actor

One row per Wikimedia Commons file: direct image URL, thumbnail, pixel size, format, license group and terms, whether credit is required, a ready-to-paste credit line, author, description and GPS coordinates. Search by keyword, list a category, look files up by title, or find images near a point.

- **URL**: https://apify.com/yadroo/wikimedia-commons-images.md
- **Developed by:** [Samat Makatov](https://apify.com/yadroo) (community)
- **Categories:** For creators, AI, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.70 / 1,000 media file rows

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
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

## Wikimedia Commons Image Search: Free Images with Credit Lines

One row per Wikimedia Commons file: direct image URL, thumbnail, pixel size, format, license group and terms, whether credit is required, a ready-to-paste credit line, author, description and GPS coordinates. Search by keyword, list a category, look files up by title, or find images near a point.

Wikimedia Commons is the media repository behind Wikipedia: on 2026-10-09 it held 149,313,708 files, and it only accepts files that anyone may reuse — commercial use and derivative works included. This actor reads its public API, so every row arrives with the license as the file page states it, the obligation that license carries, and a credit line you can paste under the picture. No API key, no account, no browser, no proxy.

### Use cases

- **An article illustration you may publish**: search your subject, keep photos at least 2000 px wide, and get the image address plus the credit line in one row.
- **Images that need no credit at all**: filter the license groups down to public domain and CC0 for print, packaging or a product UI where attribution has no place.
- **The whole contents of a curated category**: a museum's collection, a city's buildings, one plant genus — categories are curated by people, which search rankings are not.
- **Vectors for a design system**: SVG drawings of flags, icons, coats of arms and diagrams, with the license checked before the asset ships.
- **Pictures of one place**: everything geotagged within a radius of a point, for travel guides, property listings or map projects.
- **Licensing documentation for file names you already have**: paste a list of file names out of an article, a CMS or an old dataset and get the license, the author and the credit line of each.
- **A scheduled watch on new uploads**: order by newest uploads, switch `onlyNew` on, and each run returns only files it has not given you before — see [what a watch returns](#limits--faq) for what that means on the second run.
- **A balanced image set for model training**: `sortBy: random` draws a different sample every run instead of the same top hits, and every row says what you are allowed to do with the file.

### Input

Nothing is required: the prefilled values are a working run. Pick the job with `mode`, then fill the fields that mode uses.

| Field | Type | Default | Allowed values / notes |
|---|---|---|---|
| `mode` | string (enum) | `search` | `search`, `category`, `files` — see [Modes](#modes). Strict: the platform refuses any other value before the run starts |
| `queries` | string\[] | empty (prefill `["sunflower"]`) | One search per line, up to 20. Passed to the Commons search engine as typed, with its [operators](#search-operators). Mode `search` |
| `categories` | string\[] | empty (prefill `["Sunflowers"]`) | One category per line, up to 20. `Category:` prefix optional, case, spaces and underscores free, a category page address also works. Mode `category` |
| `fileTitles` | string\[] | empty (prefill two real files) | Up to 200 file titles, file page addresses or direct image addresses; `File:` prefix optional. Mode `files` |
| `mediaTypes` | string\[] (enum) | empty = every kind | `bitmap`, `drawing`, `video`, `audio`, `office`, `3d` — see [Kinds of media](#kinds-of-media). Sent to the search engine. Strict enum |
| `fileFormats` | string\[] (enum) | empty = any format | `jpeg`, `png`, `gif`, `svg`, `tiff`, `webp`, `pdf`. One format goes into the search; several are filtered on the metadata. Strict enum |
| `minWidthPx` | integer | empty | 1–30000. Minimum pixel width; part of the search, so small files never cost a row |
| `minHeightPx` | integer | empty | 1–30000. Minimum pixel height |
| `licenseGroups` | string\[] (enum) | empty = every license | `public-domain`, `cc0`, `cc-by`, `cc-by-sa`, `gfdl`, `other-free` — see [License groups](#license-groups). Checked on the file page after the metadata arrives. Strict enum |
| `latitude` | number | empty | -90 to 90, decimal degrees. Needs `longitude`; one without the other is refused before any request |
| `longitude` | number | empty | -180 to 180, decimal degrees |
| `radiusKm` | integer | 10 | 1–500. Used only with both coordinates |
| `sortBy` | string (enum) | `relevance` | `relevance`, `newest-uploads`, `oldest-uploads`, `recently-edited`, `random` — see [Order of results](#order-of-results). Strict enum |
| `addedSinceHours` | integer | empty | 1–8760. Mode `category` only: files added to the category in the last N hours (UTC) |
| `onlyNew` | boolean | `false` | Skip files this actor already delivered to you, before it reads or charges for them (page ids remembered in a named key-value store) |
| `thumbnailWidthPx` | integer | 320 | 0–2048. Width of the scaled copy in `thumbnailUrl`; 0 = no thumbnail |
| `maxItems` | integer | 50 | 1–1000. Hard cap on the rows this run writes, shared between several searches in the order you list them |
| `fields` | string\[] | empty = every column | Keep only these columns, **in this order**. `query`, `fileName`, `url`, `fileUrl`, `licenseShortName`, `found` and `fetchedAt` are always kept (first, unless you list them yourself) |

Enum fields (`mode`, `mediaTypes`, `fileFormats`, `licenseGroups`, `sortBy`) are checked by the platform before the run starts: an off-list value is refused with HTTP 400 and the list of allowed values, so the run never begins. The free-text fields are forgiving instead: `categories` and `fileTitles` accept any case, spaces or underscores, the namespace prefix or none, and page addresses; `fields` accepts any case and the `snake_case` spelling of a column (`file_url` means `fileUrl`), and a name that is no column of this actor is reported in the run status with the closest match ("did you mean licenseGroup?") instead of being dropped in silence. `queries` is handed to the search engine exactly as typed — the engine does its own stemming, and this actor never rewrites your words.

#### Modes

| mode | what it does | fields it uses | fields it ignores |
|---|---|---|---|
| `search` | Sends each entry of `queries`, and/or a point from the coordinate fields, to the Commons search engine | `queries`, `latitude`/`longitude`/`radiusKm`, all filters, `sortBy` | `categories`, `fileTitles`, `addedSinceHours` |
| `category` | Lists the files that sit directly in each category (subcategories are not followed) | `categories`, `addedSinceHours`, all filters, `sortBy` (3 of 5 values) | `queries`, `fileTitles`, coordinates |
| `files` | Reads exactly the files you name and marks what Commons does not have with `found: false` | `fileTitles`, `thumbnailWidthPx`, `fields` | the filters, `sortBy`, `queries`, `categories` |

An input the current mode does not use is named in the run's status message, so a run never pretends a value had an effect.

### Reference

#### Kinds of media

The repository is not only photographs. `mediaTypes` keeps only the kinds you want, and every row repeats the value in `mediaType`.

| value | `mediaType` in the row | what falls into it |
|---|---|---|
| `bitmap` | `BITMAP` | photographs and scans: JPEG, PNG, TIFF, GIF, WebP |
| `drawing` | `DRAWING` | vector drawings: SVG — flags, icons, coats of arms, diagrams, maps |
| `video` | `VIDEO` | video files (WebM, OGV) |
| `audio` | `AUDIO` | recordings, pronunciations, music (OGG, FLAC, WAV) |
| `office` | `OFFICE` | documents: PDF, DjVu — scanned books, reports, sheet music |
| `3d` | `3D` | 3D models (STL) |

#### File formats

`jpeg` (`image/jpeg`), `png` (`image/png`), `gif` (`image/gif`), `svg` (`image/svg+xml`), `tiff` (`image/tiff`), `webp` (`image/webp`), `pdf` (`application/pdf`). One chosen format is pushed into the search query; the search engine takes only one format at a time, so if you pick several, the actor fetches the page and keeps the matching files — the same rows, a page that can come back thinner.

#### License groups

Every file on Commons may be reused, commercially included; the groups say what you owe in return. `licenseGroup` carries the group, `licenseShortName` the license as the file page names it, `licenseCode` its machine form, `licenseUrl` the license text.

| group | what it means for you | typical `licenseShortName` |
|---|---|---|
| `public-domain` | No permission and no credit needed (expired copyright, work of a government that releases it, author's dedication) | `Public domain`, `PD-US` |
| `cc0` | The author waived everything; a credit is welcome but not required | `CC0` |
| `cc-by` | Use it anywhere, including commercially, but name the author and the license | `CC BY 4.0` |
| `cc-by-sa` | Same, and anything you build on it must stay under a compatible free license | `CC BY-SA 4.0`, `CC BY-SA 3.0` |
| `gfdl` | The GNU Free Documentation License: credit plus the license text travels with the file | `GFDL 1.2` |
| `other-free` | Another free license (attribution-only licenses, open government licenses, free software licenses used for diagrams) | `Attribution`, `OGL 3` |
| `unknown` | The file page states no machine-readable license. Rows keep it; `licenseGroups` never selects it | — |

`attributionRequired` says whether a credit line is obligatory, and `attributionText` is one already composed: author, license, "via Wikimedia Commons" and the file page address. The license covers copyright only — people shown in a photograph, trademarks, and buildings in countries without freedom of panorama stay your responsibility.

#### Order of results

| value | `search` mode | `category` mode |
|---|---|---|
| `relevance` | the ranking of the Commons search box | the category's own order (its sort keys) |
| `newest-uploads` | file pages created most recently first | files added to the category most recently first |
| `oldest-uploads` | oldest file pages first | files added to the category earliest first |
| `recently-edited` | file pages edited most recently first | not available: falls back to the category order (the status says so) |
| `random` | a different sample each run | not available: falls back to the category order (the status says so) |

#### Search operators

`queries` reaches the engine behind the Commons search box, so its operators work:

- `"solar eclipse"` — the phrase, not the two words apart
- `cat -drawing` — exclude a word
- `incategory:"Sunflowers"` — only files in that category
- `insource:helianthus` — look inside the file description page
- `coat of arms -france` — combine them freely

The actor adds the keywords for your filters itself (`filetype:`, `filemime:`, `filew:`, `fileh:`, `nearcoord:`), so you do not have to.

### Examples

**Large, freely licensed photographs for an article**

```json
{ "mode": "search", "queries": ["sunflower"], "mediaTypes": ["bitmap"], "minWidthPx": 2000, "maxItems": 15 }
```

**Public domain and CC0 only, where no credit line is possible**

```json
{ "mode": "search", "queries": ["antique map"], "mediaTypes": ["bitmap"], "licenseGroups": ["public-domain", "cc0"], "minWidthPx": 1200, "maxItems": 15 }
```

**Everything in one curated category**

```json
{ "mode": "category", "categories": ["Sunflowers"], "mediaTypes": ["bitmap"], "maxItems": 15 }
```

**Geotagged pictures within 5 km of the Eiffel Tower**

```json
{ "mode": "search", "latitude": 48.8584, "longitude": 2.2945, "radiusKm": 5, "mediaTypes": ["bitmap"], "maxItems": 15 }
```

**Free SVG vectors with the license checked**

```json
{ "mode": "search", "queries": ["coat of arms"], "mediaTypes": ["drawing"], "fileFormats": ["svg"], "maxItems": 15 }
```

**License lookup for file names you already have, three columns only**

```json
{ "mode": "files", "fileTitles": ["File:Tour Eiffel Wikimedia Commons.jpg", "Suncokret 22.jpg", "https://commons.wikimedia.org/wiki/File:World_Map_1689.JPG"], "fields": ["licenseShortName", "attributionText", "widthPx"] }
```

**Watch a topic on a schedule (only new files each run)**

```json
{ "mode": "search", "queries": ["landscape photograph"], "mediaTypes": ["bitmap"], "sortBy": "newest-uploads", "onlyNew": true, "maxItems": 50 }
```

### Output

One row per file. A real row, the first one of cloud run `4bR9e8u1cdadSQZxq` on 2026-10-10 (long lists trimmed with "…"):

```json
{
  "query": "sunflower",
  "fileName": "Sunflower head 2026 G1.jpg",
  "pageId": 196721727,
  "url": "https://commons.wikimedia.org/wiki/File:Sunflower_head_2026_G1.jpg",
  "fileUrl": "https://upload.wikimedia.org/wikipedia/commons/3/3f/Sunflower_head_2026_G1.jpg",
  "thumbnailUrl": "https://thumb.wikimedia.org/wikipedia/commons/thumb/3/3f/Sunflower_head_2026_G1.jpg/330px-Sunflower_head_2026_G1.jpg",
  "thumbnailWidthPx": 320,
  "thumbnailHeightPx": 213,
  "widthPx": 6000,
  "heightPx": 4000,
  "fileSizeBytes": 5667006,
  "mediaType": "BITMAP",
  "mimeType": "image/jpeg",
  "licenseShortName": "CC BY 4.0",
  "licenseCode": "cc-by-4.0",
  "licenseGroup": "cc-by",
  "licenseUrl": "https://creativecommons.org/licenses/by/4.0",
  "usageTerms": "Creative Commons Attribution 4.0",
  "restrictions": null,
  "attributionRequired": true,
  "attributionText": "George Chernilevsky, CC BY 4.0, via Wikimedia Commons (https://commons.wikimedia.org/wiki/File:Sunflower_head_2026_G1.jpg)",
  "author": "George Chernilevsky",
  "credit": "Own work",
  "description": "Sunflower head. Ukraine, Vinnytsia Rajon.",
  "dateCreated": "2026-07-21 08:55:25",
  "fileVersionAt": "2026-08-03T11:18:48.000Z",
  "uploadedBy": "George Chernilevsky",
  "categories": ["Helianthus annuus", "Featured pictures of flowers", "Sunflowers in Ukraine", "…"],
  "latitude": null,
  "longitude": null,
  "matchSnippet": "org/licenses/by/4.0CC BY 4.0 Creative Commons Attribution 4.0 truetrue English Sunflower head. Ukraine, Vinnytsia Rajon. …",
  "found": true,
  "fetchedAt": "2026-10-10T00:14:13.561Z"
}
```

| Field | Type | Meaning |
|---|---|---|
| `query` | string | The search words, category name or file title this row came from. Always filled |
| `fileName` | string | File name without the `File:` prefix. Always filled |
| `pageId` | number | Permanent file page id on Commons. Filled when the file was found |
| `url` | string | The file page, where license and description are shown. Always filled |
| `fileUrl` | string | The file itself, on the repository upload host. Filled when found |
| `thumbnailUrl`, `thumbnailWidthPx`, `thumbnailHeightPx` | string, number | A scaled copy and its real size. Filled when `thumbnailWidthPx` > 0 |
| `widthPx`, `heightPx`, `fileSizeBytes` | number | Pixel size and byte size. Filled when found |
| `mediaType` | string | `BITMAP`, `DRAWING`, `VIDEO`, `AUDIO`, `OFFICE`, `3D`. Filled when found |
| `mimeType` | string | Format as a mime type, e.g. `image/jpeg`. Filled when found |
| `licenseShortName` | string | The license as the file page names it. Filled for almost every file |
| `licenseCode` | string | Machine form of the license (`cc-by-sa-4.0`, `pd`, `cc0`); null on files whose page gives only a name |
| `licenseGroup` | string | The obligation group, normalised by this actor. Filled when found |
| `licenseUrl` | string | Address of the license text, where the file page gives one |
| `usageTerms` | string | The full license name as the file page writes it |
| `restrictions` | string | Non-copyright warnings (trademark, personality rights); usually empty |
| `attributionRequired` | boolean | Whether a credit line is obligatory. False for public domain and CC0 |
| `attributionText` | string | A credit line ready to paste: author, license, repository, file page |
| `author` | string | Author as the file page states it, markup removed |
| `credit` | string | Where the file came from ("Own work", a museum, another site) |
| `description` | string | The file description, markup removed, cut at 1000 characters |
| `dateCreated` | string | When the work was made, as the file page states it — often a date, sometimes free text |
| `fileVersionAt` | string | When the current version was uploaded, ISO 8601 UTC |
| `uploadedBy` | string | Commons account that uploaded the current version |
| `categories` | string\[] | Categories the file page is in |
| `latitude`, `longitude` | number | Coordinates the uploader geotagged the file with; null when it has none |
| `matchSnippet` | string | The search engine's highlight for this file. Keyword searches only |
| `found` | boolean | False only on the marker row an unknown file title or category produces. Always filled |
| `fetchedAt` | string | When this run read the file, ISO 8601 UTC. Always filled |

Dataset views: **Images** (preview, file, description, size, format, license, credit required, author, links), **License and credit** (license name, group, code, text address, usage terms, credit required, credit line, author, source, restrictions), **File and place** (search, kind, format, size, bytes, created, file version, coordinates, fetched).

Every run also writes a `SUMMARY` record into its default key-value store: rows delivered, files read, matching files the engine reported, category file counts, what each filter dropped, requests made, deferred and failed requests, and the inputs the mode ignored.

With `onlyNew` on, the `SUMMARY.onlyNew` object holds the whole bookkeeping of the watch in one place, so you can check the run against itself: which store and record hold the memory, `filesRememberedBeforeThisRun` and `filesRememberedNow`, `skippedAsAlreadyDelivered` (the figure the run status quotes) split into `skippedBeforeReadingMetadata` and `skippedWhenStoring`, plus `filesRead` and `delivered`. Two counters next to it, `skippedAsSeen` and `skippedAsAlreadyStored`, belong to the shared run machinery and count only rows stopped at the moment of writing; because this actor recognises an already delivered file from its listing entry and drops it before reading it, those two normally stay at `0` on a healthy watch while `skippedAsAlreadyDelivered` carries the count. The `note` field inside `onlyNew` says the same thing in the record itself.

### Use it from code / agents

```bash
curl -X POST "https://api.apify.com/v2/acts/yadroo~wikimedia-commons-images/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"mode":"search","queries":["sunflower"],"mediaTypes":["bitmap"],"minWidthPx":2000,"maxItems":15}'
```

```js
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('yadroo/wikimedia-commons-images').call({
  mode: 'files',
  fileTitles: ['File:Tour Eiffel Wikimedia Commons.jpg'],
  fields: ['licenseShortName', 'attributionText'],
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

```python
from apify_client import ApifyClient
client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor("yadroo/wikimedia-commons-images").call(run_input={
    "mode": "category", "categories": ["Sunflowers"], "maxItems": 50,
})
items = client.dataset(run["defaultDatasetId"]).list_items().items
```

MCP: add `https://mcp.apify.com` to Claude / Cursor / any MCP client and call the `yadroo/wikimedia-commons-images` tool with the same JSON input. The input schema describes every field and its allowed values, so an agent can fill it without further documentation, and each run's `SUMMARY` record tells the agent what the run did.

### Pricing

Pay per event: **$0.001 per run start + $0.001 per file row**. Every run pays the start event, also a run that returns nothing.

| Run | Cost |
|---|---|
| 15 rows (one search, the examples above) | 0.001 + 15 × 0.001 = **$0.016** |
| 50 rows (the default `maxItems`) | 0.001 + 50 × 0.001 = **$0.051** |
| 1000 rows (the maximum one run writes) | 0.001 + 1000 × 0.001 = **$1.001** |

Lower tiers pay less per row automatically (Bronze $0.0009, Silver $0.0008, Gold and above $0.0007 per row); the start event is flat.

A `found: false` marker row — a file title or a category Commons does not have — is a dataset row and is charged like any other, because it carries the answer to that part of your input. Files the filters drop are not rows and cost nothing. The run also stops at the "Maximum cost per run" you set when you start it: it keeps every row you paid for, writes them to the dataset and says so in its status message ("Stopped at your spending limit: N rows delivered").

### Limits & FAQ

**What Commons does not hold.** It is not a stock library. There is hardly any current product photography, press imagery, celebrity or model photography, and almost no logos in current use. What it does hold in depth: landscapes, plants and animals, buildings and streets, maps and historical scans, portraits of public figures, flags and coats of arms, diagrams, and public-sector imagery. Its relevance ranking is also weaker than a web search engine's — a broad word can return a map or a diagram where you expected a photograph, which is what `mediaTypes`, `fileFormats`, `minWidthPx` and `matchSnippet` are there for.

**Speed and what fits in one run.** Measured in the Apify cloud at 256 MB on 2026-10-10: a 50-row run takes 2 requests and 4–6 seconds end to end; a 15-row run takes 5 seconds; a run that read 1 050 files' metadata used 42 requests and 44 seconds, with no request throttled or deferred. So one page of 50 files costs 2 requests (the listing and the metadata), and the sustained pace is one request per second to `commons.wikimedia.org`, retries included — the pace Wikimedia's API etiquette asks of automated clients. `maxlag=5` lets the API defer us while its databases lag; a deferred request is waited out and repeated rather than pushed harder. At that pace the largest input the schema allows (1 000 rows) needs about 45 seconds and sits comfortably inside the default 900-second run timeout. If a run does come close to its timeout — a license filter that drops most of what it reads, say — it stops early, keeps every row it had already read and says so: "Stopped before the run timeout: N rows saved." Verified on 2026-10-10 with a 60-second run that read 1 050 files, kept the 4 matching ones and ended SUCCEEDED with that status.

**Rate limits.** The API publishes no quota and we have seen no 429 from it. A run that asks for much more than it can read in one go stops after 160 requests with a status telling you to narrow the search or lower `maxItems`.

**Subcategories are not followed.** `category` mode lists what sits directly in the category; the status message says how many files the category holds and how many subcategories it has, so you can list those too if you want them. Some categories — `Eiffel Tower`, for one — hold no file of their own at all and only group subcategories; a run on one of those writes no row and says exactly that, so you know to name the subcategory you meant.

**No upload-date filter in keyword search.** The search engine has none, so `addedSinceHours` works in `category` mode only. To watch a topic, use `sortBy: newest-uploads` with `onlyNew`.

**What a watch with `onlyNew` returns, run after run.** `onlyNew` remembers the page id of every file it has written for you and skips that file in later runs — before the run reads its metadata, so you are never charged for it twice. What it does **not** mean is "only files uploaded since the last run": the memory is of what you were given, not of a point in time. So a watch of `sortBy: newest-uploads` with `maxItems: 50` gives you the 50 newest files on the first run; on the second run those 50 are skipped and you get the next 50 you have not been given — older uploads, deeper in the same result list — and so on until the watch has worked through everything the search matches, after which a run returns only what has been uploaded since. That is what you want for building up a collection; if instead you only ever want the last few hours, watch a category with `addedSinceHours` (which is a real time window) or keep `maxItems` at the number of new files you expect per run. Every run says how many files it skipped in its status message and in `SUMMARY.onlyNew.skippedAsAlreadyDelivered`, next to how many files the memory held before the run and holds now — see [Output](#output) for the full record and for why the run-wide `skippedAsSeen` counter reads `0` while a watch is working normally.

**`dateCreated` is not always a date.** It is whatever the uploader wrote on the file page: `2009-06-01`, `2008-11`, `19th century`, sometimes a sentence. `fileVersionAt` is always a real UTC timestamp.

**Geotags exist only where an uploader added them.** Coordinate search finds the files that carry coordinates, which is a minority of the repository, and the coordinates may describe where the camera stood or what it pointed at.

**The license covers copyright only.** People depicted, trademarks, and freedom of panorama in the country where a building stands are not covered by the file's license and remain yours to judge. `restrictions` repeats any warning the file page carries.

**Empty results are never silent.** A search that matches nothing, a category that does not exist, a file title Commons has never had: the run says which input it was, and `files` mode writes a `found: false` row for it instead of dropping it.

Questions or problems: open an issue on the Issues tab of this actor.

***

Made by **Yadroo**. Sibling actors: [wikipedia-search](https://apify.com/yadroo/wikipedia-search), [openlibrary-books](https://apify.com/yadroo/openlibrary-books), [openalex-works](https://apify.com/yadroo/openalex-works), [nasa-eonet-events](https://apify.com/yadroo/nasa-eonet-events), [data-gov-datasets](https://apify.com/yadroo/data-gov-datasets).

# Actor input Schema

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

Pick the job. `search` sends each entry of `queries` - and/or a point from the coordinate fields - to the Commons search engine and applies the filters below. `category` lists the files of each name in `categories`. `files` reads exactly the files named in `fileTitles` and marks anything Commons does not have with `found: false` instead of dropping it. The filter fields are ignored in `files` mode and the run status says so.

## `queries` (type: `array`):

One search per line, up to 20 per run; each row names the search that found it in `query`. The words go to the engine behind the Commons search box, so its operators work: quotation marks for a phrase (`"solar eclipse"`), a leading minus to exclude a word (`cat -drawing`), `incategory:"Sunflowers"` to stay inside one category, `insource:` to look in the file description. Words are passed on as typed - the engine does its own stemming and suggests nothing. Used when `mode` is `search`; may be left empty when you search by coordinates instead.

## `categories` (type: `array`):

One category per line: `Sunflowers`, `Category:Cats by country`, or the address of a category page. The `Category:` prefix is optional and case, spaces and underscores do not matter. Only the files listed directly in the category are returned - subcategories are not followed, and a category that does not exist yields one row with `found: false` so you see which name was wrong. Used when `mode` is `category`.

## `fileTitles` (type: `array`):

Up to 200 files per run, read 50 at a time: `File:Suncokret 22.jpg`, `Suncokret 22.jpg` or the address of the file page. The `File:` prefix is optional, underscores and spaces are interchangeable, and a title Commons does not know comes back as a row with `found: false` rather than ending the run. Used when `mode` is `files` - handy for turning a list of file names from a Wikipedia article or a dataset into licensing rows.

## `mediaTypes` (type: `array`):

Keep only these kinds of file; the filter is sent to the search engine, so a narrow pick does not waste rows. Leave it empty to get everything Commons holds for the search - the repository is not only photographs, it also holds maps, scanned books, sound recordings, video and 3D models. Every row repeats the value in `mediaType`.

## `fileFormats` (type: `array`):

Narrower than `mediaTypes`: keep only these formats, matched on the file's media type as the repository records it (`image/jpeg`, `image/svg+xml`, `application/pdf` and so on). Empty means any format. Useful when a pipeline only accepts one container - SVG for icons that must scale, JPEG for photo training sets.

## `minWidthPx` (type: `integer`):

Keep files at least this many pixels wide. The size filter is part of the search query, so small thumbnails never reach your dataset and never cost a row. Rough guides: 1200 for a blog image, 2000 for print at a readable size, 4000 for a full-page photograph. Vector drawings have a nominal width that is usually small - leave this empty when you ask for SVG.

## `minHeightPx` (type: `integer`):

Keep files at least this many pixels tall, combined with `minWidthPx` when you set both. Use it alone for portrait-shaped images.

## `licenseGroups` (type: `array`):

Keep only files whose license falls into one of these groups; every row says which group it is in (`licenseGroup`) next to the license as the file page states it (`licenseShortName`, `licenseCode`, `licenseUrl`). The groups are read from the file page after the metadata arrives, so picking one costs no extra requests but can thin out a page of results. Commons rejects files that forbid commercial use or derivative works, so no row is limited that way. Rights that do not come from the license - people shown in a photo, trademarks, buildings in countries without freedom of panorama - are not covered here and stay your responsibility.

## `latitude` (type: `number`):

Latitude of a point to search around, decimal degrees, north positive. Use it with `longitude` and `radiusKm` to get only files whose own coordinates sit in that circle - the way to collect pictures of one street, site or landmark. Works with or without search words, and the coordinates of each file come back in `latitude` and `longitude`. Only files whose uploader geotagged them can match.

## `longitude` (type: `number`):

Longitude of the centre point, decimal degrees, east positive. Needed together with `latitude`; one without the other is refused with a clear message before any request goes out.

## `radiusKm` (type: `integer`):

How far from the centre point to look, in kilometres. Ignored unless `latitude` and `longitude` are both set. A city centre is well covered at 2-5 km; a whole metropolitan area needs 25-50.

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

`relevance` is the ranking of the Commons search box. `newest-uploads` and `oldest-uploads` go by the day the file page was created, which is what you want for watching a topic or for digging out historical uploads. `random` draws a different sample every run - useful for building a balanced training set instead of the same top results. In `category` mode only `relevance` (the category's own order), `newest-uploads` and `oldest-uploads` (the time the file was added to the category) exist; the other two fall back to the category order and the run status says so.

## `addedSinceHours` (type: `integer`):

Category mode only: keep files that were added to the category within this many hours, counted in UTC - 24 for a day, 168 for a week. In keyword search this is ignored (the search engine has no date filter) and the run status names it; use `sortBy: newest-uploads` with `onlyNew` there instead.

## `onlyNew` (type: `boolean`):

For scheduled runs: skip files this actor already returned to you. The page id of every delivered file is kept in the actor's key-value store, so the next run only pays for and returns what is new. Pair it with `sortBy: newest-uploads` to watch a topic or a category. The first run has nothing remembered and returns everything that matches.

## `thumbnailWidthPx` (type: `integer`):

Width of the scaled copy whose address goes into `thumbnailUrl`, together with its real `thumbnailWidthPx` and `thumbnailHeightPx`. 0 asks for no thumbnail at all. The repository scales at most 50 images per request, which is exactly the page size this actor uses. A file narrower than the width you ask for is handed back at its own size - nothing is blown up.

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

Hard cap on the rows this run writes, and so on what it costs. Rows are collected 50 at a time; with several searches the cap is shared between them in the order you listed them. The run also stops at the maximum cost per run you set on the platform, and says in its status how many rows it saved.

## `fields` (type: `array`):

Keep only the named columns, in the order given - for example `fileUrl, licenseShortName, attributionText`. Empty = every column. `query`, `fileName`, `url`, `fileUrl`, `licenseShortName`, `found` and `fetchedAt` are always present; name one of them here and it moves to the position you gave it. Unknown names are reported in the run's status message instead of being dropped in silence.

## Actor input object example

```json
{
  "mode": "search",
  "queries": [
    "sunflower"
  ],
  "categories": [
    "Sunflowers"
  ],
  "fileTitles": [
    "File:Tour Eiffel Wikimedia Commons.jpg",
    "File:Suncokret 22.jpg"
  ],
  "mediaTypes": [
    "bitmap"
  ],
  "radiusKm": 10,
  "sortBy": "relevance",
  "onlyNew": false,
  "thumbnailWidthPx": 320,
  "maxItems": 50
}
```

# 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 = {
    "queries": [
        "sunflower"
    ],
    "categories": [
        "Sunflowers"
    ],
    "fileTitles": [
        "File:Tour Eiffel Wikimedia Commons.jpg",
        "File:Suncokret 22.jpg"
    ],
    "mediaTypes": [
        "bitmap"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("yadroo/wikimedia-commons-images").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 = {
    "queries": ["sunflower"],
    "categories": ["Sunflowers"],
    "fileTitles": [
        "File:Tour Eiffel Wikimedia Commons.jpg",
        "File:Suncokret 22.jpg",
    ],
    "mediaTypes": ["bitmap"],
}

# Run the Actor and wait for it to finish
run = client.actor("yadroo/wikimedia-commons-images").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 '{
  "queries": [
    "sunflower"
  ],
  "categories": [
    "Sunflowers"
  ],
  "fileTitles": [
    "File:Tour Eiffel Wikimedia Commons.jpg",
    "File:Suncokret 22.jpg"
  ],
  "mediaTypes": [
    "bitmap"
  ]
}' |
apify call yadroo/wikimedia-commons-images --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,yadroo/wikimedia-commons-images"
        }
    }
}
```

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/fpGv1OgZBmSZYggQU/builds/wMZxaQPp3DmAoNN85/openapi.json
