# Free Stock Photo Search: Openverse, Pexels, Unsplash (`ucchiii/stock-photos-openverse-search`) Actor

Search openly licensed and free-to-use images in one unified format: Openverse (no key), plus Pexels and Unsplash with your own API key. Every row has the license, creator and a ready-to-paste attribution text. No downloads. Empty input = free demo. $1 per 1,000 images.

- **URL**: https://apify.com/ucchiii/stock-photos-openverse-search.md
- **Developed by:** [Actor Factory](https://apify.com/ucchiii) (community)
- **Categories:** Automation, Other
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.70 / 1,000 image result returneds

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

## Free Stock Photo Search — Openverse, Pexels and Unsplash in one table, with license and attribution

**Search openly licensed and free-to-use images and get one flat row per image: image URL, thumbnail, size, creator, license and a ready-to-paste attribution text.** Built for bloggers, marketers, designers, newsletter and presentation makers, and developers who need legally clear images and do not want to read three different license pages.

- **Openverse** (Creative Commons and public-domain images from many collections): no key needed
- **Pexels** and **Unsplash**: optional, with your own free API key
- Filter by **commercial use**, **modifications allowed** (Openverse) and **orientation**
- Images are **not downloaded**: you get the URLs returned by each API

**Try it free: leave input empty and press Start.** The demo returns 10 Openverse images for "sunset" and is not charged.

Example output (shortened, illustrative):

| source | title | creator | license | attributionText |
|---|---|---|---|---|
| openverse | Mountain lake at dawn | Jane Doe | CC BY-SA 4.0 | "Mountain lake at dawn" by Jane Doe is licensed under CC BY-SA 4.0. |
| pexels | A calm lake | Max Mustermann | Pexels License | Photo by Max Mustermann on Pexels |
| unsplash | blue lake between mountains | Ann Lee | Unsplash License | Photo by Ann Lee on Unsplash |

**Price: $1 per 1,000 images ($0.001 each).** Example: 10 queries x 10 images = 100 rows = $0.10. Failed requests, searches with no results and the demo are free.

### Input

- `queries`: one search per line (up to 50)
- `sources`: `openverse` (default), `pexels`, `unsplash`. Pexels and Unsplash run only when selected **and** the key is given (`pexelsApiKey`, `unsplashAccessKey`, both secret)
- `commercialUseOnly`, `modificationsAllowed`: license filters, Openverse only (Pexels and Unsplash licenses already allow both)
- `orientation`: `any`, `landscape`, `portrait`, `square`
- `maxResultsPerQuery`: 1 to 500, default 10, counted per query and per source
- `unsplashAppName`: name of your Unsplash app, used as `utm_source` in Unsplash links

### Output fields (one row per image)

`source`, `id`, `title`, `imageUrl`, `thumbnailUrl`, `width`, `height`, `pageUrl`, `creator`, `creatorUrl`, `license`, `licenseVersion`, `licenseUrl`, `attributionText`, `query`.
Extras: `attributionHtml` (same text with the required links, HTML-escaped), `provider` (Openverse: the collection, for example flickr), `downloadTrackingUrl` (Unsplash only, see below).

### Attribution and license rules (read before you publish)

- **Openverse**: each image has its own Creative Commons license (or CC0 / Public Domain Mark). `attributionText` is the sentence Openverse generates for the image. Most CC licenses require credit, a link to the license and a note if you changed the image. Check `license` and `licenseUrl` for each image; NC means non-commercial only, ND means no changes.
- **Pexels**: free to use under the Pexels License. Credit is not legally required but the API terms ask you to credit the photographer and link to Pexels where you show API images; `attributionText` and `attributionHtml` do that.
- **Unsplash**: free to use under the Unsplash License. The API guidelines ask you to **hotlink the URLs returned** (`imageUrl`, `thumbnailUrl`: do not rewrite or re-host them) and to credit the photographer and Unsplash with links that carry `utm_source=<your app>&utm_medium=referral`. `creatorUrl`, `pageUrl` and `attributionHtml` already include it. When a photo is actually used or downloaded by your users, call `downloadTrackingUrl` (with your Authorization header) as the Unsplash guidelines require; this Actor does not call it because it downloads nothing.
- You need your own Pexels / Unsplash account and key and you accept their API terms. Unsplash demo apps are limited to 50 requests per hour, Pexels to 200 per hour by default.

### How it works

- Calls the Openverse API (`api.openverse.org/v1/images/`) anonymously, 20 images per call, with a pause of 1.5 s between calls. On HTTP 429 the Actor waits (Retry-After if given, else 10 s, 30 s, 60 s) and retries up to 3 times; if the limit is still active it stops that source and keeps the rows already collected. Anonymous Openverse limits are strict, so very large runs may return fewer images than requested.
- Pexels (`api.pexels.com/v1/search`, up to 80 per call) and Unsplash (`api.unsplash.com/search/photos`, up to 30 per call) use the same pacing and retry rules.
- Your keys are used only for the run and are never logged or written to the output.
- A row is charged only after it is written. If you set a maximum cost per run, the run stops at the limit.

### Price

| Event | Price |
|---|---|
| `image` (one image row written) | $0.001 |

**Not charged:** failed requests, searches with no results, skipped sources and the empty-input demo.

### Notes

Images belong to their creators; the license shown per image is what the source reports. This Actor does not verify licenses and is not legal advice. Pexels and Unsplash are trademarks of their owners; this Actor is not affiliated with them or with Openverse.

### More tools from the same developer

- [Website Screenshot API](https://apify.com/ucchiii/website-screenshot) — screenshots and PDFs of web pages: full page, mobile, element, no cookie banners
- [OCR Image to Text](https://apify.com/ucchiii/ocr-image-text) — OCR for image URLs and scanned PDFs with built-in Tesseract: text, confidence and line boxes in 8 languages
- [Google Sheets Export](https://apify.com/ucchiii/google-sheets-export) — export an Apify dataset to Google Sheets
- [Dataset Merge & Dedup](https://apify.com/ucchiii/dataset-dedup-merge) — merge Apify datasets and remove duplicates by the fields you choose

# Actor input Schema

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

What to look for, one query per line, for example mountain lake or coffee shop. Up to 50 queries. Leave empty for a free demo (10 Openverse results for sunset, not charged).

## `sources` (type: `array`):

Where to search. Openverse needs no key. Pexels and Unsplash are only used when they are selected here AND you entered the matching key below; otherwise they are skipped with a log warning.

## `pexelsApiKey` (type: `string`):

Your own free Pexels API key (pexels.com/api). Used only for this run to call the Pexels API; never stored by us and never written to logs or output. Needed only if you select Pexels.

## `unsplashAccessKey` (type: `string`):

Your own Unsplash API Access Key (unsplash.com/developers, the Access Key of your app, not the secret key). Used only for this run; never stored by us and never written to logs or output. Needed only if you select Unsplash.

## `unsplashAppName` (type: `string`):

Unsplash asks that links to photos and photographers carry utm_source=<your app name>\&utm_medium=referral. Enter the name of your Unsplash application. Default stock-photos-search.

## `commercialUseOnly` (type: `boolean`):

true: Openverse only returns images whose license allows commercial use (no NC licenses). Default false. Pexels and Unsplash licenses already allow commercial use, so this filter changes only Openverse results.

## `modificationsAllowed` (type: `boolean`):

true: Openverse only returns images whose license allows modification and adaptation (no ND licenses). Default false. Affects only Openverse results.

## `orientation` (type: `string`):

Filter by image shape. Openverse maps this to wide, tall or square; Pexels and Unsplash use their own orientation filter. Default any.

## `maxResultsPerQuery` (type: `integer`):

Maximum images for each query from each selected source (1 to 500, default 10). With two sources, 10 means up to 20 rows per query. Sources may return fewer: Openverse limits how deep anonymous searches can page.

## Actor input object example

```json
{
  "queries": [
    "mountain lake"
  ],
  "sources": [
    "openverse"
  ],
  "unsplashAppName": "stock-photos-search",
  "commercialUseOnly": false,
  "modificationsAllowed": false,
  "orientation": "any",
  "maxResultsPerQuery": 10
}
```

# Actor output Schema

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

One row per image: source, id, title, imageUrl, thumbnailUrl, width, height, pageUrl, creator, creatorUrl, license, licenseVersion, licenseUrl, attributionText, attributionHtml, query, plus provider (Openverse) and downloadTrackingUrl (Unsplash).

# 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": [
        "mountain lake"
    ],
    "maxResultsPerQuery": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("ucchiii/stock-photos-openverse-search").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": ["mountain lake"],
    "maxResultsPerQuery": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("ucchiii/stock-photos-openverse-search").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": [
    "mountain lake"
  ],
  "maxResultsPerQuery": 10
}' |
apify call ucchiii/stock-photos-openverse-search --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,ucchiii/stock-photos-openverse-search"
        }
    }
}
```

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/iy6Z3W1gGLvSXrXJi/builds/LJRs9vhA0hnBGqDlF/openapi.json
