# Unsplash Photo Search by Topic, Collection & Creator (`dataspryva/unsplash-photo-search-by-topic-collection`) Actor

Searches Unsplash by keyword or browses a curated topic, numbered collection, or photographer's portfolio, returning photo dimensions, orientation, license, and photographer credit.

- **URL**: https://apify.com/dataspryva/unsplash-photo-search-by-topic-collection.md
- **Developed by:** [Dataspryva](https://apify.com/dataspryva) (community)
- **Categories:** Automation, Developer tools, Social media
- **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

## Unsplash Photo Search by Topic, Collection & Creator

Scrape **Unsplash** photos by keyword, curated topic, collection, photographer, photo ID, or any supported Unsplash URL. Each row returns five image sizes, full photographer attribution, dimensions, orientation, dominant colour, BlurHash, likes, and optional tags, camera EXIF, geolocation, topic memberships, and view/download counts. No API key, login, or proxy setup.

### What this actor does

- **Seven ways to source photos:** keyword search, curated topics, numbered collections, photographer portfolios, specific photos, related photos, or a mixed list of pasted Unsplash URLs.
- **Search filters:** orientation, dominant colour, best-match or newest-first ordering, and a free-photos-only switch.
- **Portfolio ordering:** pinned (profile order), newest, oldest, or most popular.
- **Rich metadata:** five image resolutions, photographer name/username/profile/socials, dimensions, colour, BlurHash, likes, and timestamps on every row.
- **Optional detail pass:** adds tags, camera EXIF, place names and coordinates, topic memberships, and real view/download counts for free photos.
- **Optional photographer totals:** each photographer's lifetime views and downloads.
- **Empty fields are omitted** — a photo without EXIF or a tagged location simply has no such field.

### Output per photo

**Identity and content**

- `id`, `slug` — Unsplash photo ID and descriptive slug
- `title`, `description`, `altDescription`, `shortDescription` — captions and alt text when available
- `width`, `height`, `aspectRatio`, `orientation` — native dimensions and `landscape` / `portrait` / `squarish`
- `dominantColor`, `blurHash`, `likes`
- `isPremium`, `premium`, `plus` — Unsplash+ flags
- `datePublished`, `updatedAt`, `promotedAt`

**Images**

- `imageUrl` — regular 1080px image
- `thumbnailUrl` — 200px thumbnail
- `imageUrls.raw`, `imageUrls.full`, `imageUrls.regular`, `imageUrls.small`, `imageUrls.thumb`, `imageUrls.smallS3` — every size in one object (`smallS3` is included for premium photos when the S3 copy responds; the actor checks up to 100 premium photos per details run and falls back to the always-present `plus.unsplash.com` variants when it does not)

**Photographer attribution**

- `photographerId`, `photographerUsername`, `photographerName`, `photographerFirstName`, `photographerLastName`
- `photographerBio`, `photographerLocation`, `photographerPortfolioUrl`, `photographerProfileUrl`
- `photographerInstagram`, `photographerTwitter`, `photographerForHire`
- `photographerTotalPhotos`, `photographerTotalFreePhotos`, `photographerTotalLikes`, `photographerTotalCollections`, `photographerProfileImageUrl`
- `photographerTotalViews`, `photographerTotalDownloads` — when photographer totals are enabled

**Photo details (when enabled)**

- `tags` — up to 20 keyword tags
- `topics` — `{id, slug, title}` memberships
- `exif.make`, `exif.model`, `exif.name`, `exif.exposureTime`, `exif.aperture`, `exif.focalLength`, `exif.iso`
- `location.name`, `location.city`, `location.country`, `location.latitude`, `location.longitude`
- `locationName` — the place name as a flat field
- `views`, `downloads` — free photos only; Unsplash does not publish stats for premium photos

**Licence, downloads, provenance**

- `license` — Unsplash License or Unsplash+ License
- `downloadUrl` — a direct CDN image file that opens without a browser session
- `downloadPageUrl` — Unsplash's tracked download page
- `sourceUrl` — the photo's page on unsplash.com
- `sourceType`, `sourceValue`, `sourceTitle` — which query, topic, collection, photographer, or photo produced the row
- `recordType` — always `photo`
- `scrapedAt` — collection timestamp

### Input

| Field | Type | Default | Description |
| --- | --- | --- | --- |
| `mode` | String (select) | `search` | `search`, `byTopic`, `byCollection`, `byUser`, `byPhoto`, `byRelated`, or `byUrls`. |
| `searchQueries` | Array | `["nature"]` | Keywords to search (mode=search). |
| `topicSlugs` | Array (select) | – | Curated topics (mode=byTopic). |
| `collectionIds` | Array | – | Collection IDs or URLs (mode=byCollection). |
| `usernames` | Array | – | Photographer usernames or profile URLs (mode=byUser). |
| `photoIds` | Array | – | Photo IDs, slugs, or photo URLs (mode=byPhoto or byRelated). |
| `startUrls` | Array | – | Mixed Unsplash search/topic/collection/photographer/photo URLs (mode=byUrls). |
| `orientation` | String (select) | `any` | `any`, `landscape`, `portrait`, or `squarish`. Exact filter in every mode. |
| `color` | String (select) | `any` | Dominant colour for keyword searches only. |
| `freeOnly` | Boolean | false | Exclude Unsplash+ photos. |
| `searchOrderBy` | String (select) | `relevant` | `relevant` or `latest` (mode=search). |
| `userOrderBy` | String (select) | `pinned` | `pinned`, `latest`, `oldest`, or `popular` (mode=byUser). |
| `includePhotoDetails` | Boolean | false | Add tags, EXIF, location, topics, and free-photo stats. |
| `includePhotographerStats` | Boolean | false | Add lifetime views/downloads for each photographer. |
| `maxItems` | Integer | 20 | Maximum photos across all sources (1-1000). |

#### Example: keyword search with filters

```json
{
  "mode": "search",
  "searchQueries": ["mountain sunrise", "coastal fog"],
  "orientation": "landscape",
  "color": "blue",
  "freeOnly": true,
  "searchOrderBy": "latest",
  "maxItems": 80
}
```

#### Example: a curated topic with details

```json
{
  "mode": "byTopic",
  "topicSlugs": ["nature", "wallpapers"],
  "orientation": "portrait",
  "includePhotoDetails": true,
  "maxItems": 40
}
```

#### Example: a collection and a photographer together

```json
{
  "mode": "byUrls",
  "startUrls": [
    { "url": "https://unsplash.com/collections/1065396" },
    { "url": "https://unsplash.com/@v2osk" }
  ],
  "userOrderBy": "latest",
  "maxItems": 50
}
```

#### Example: one photo with everything

```json
{
  "mode": "byPhoto",
  "photoIds": ["fWBZ9r4vO9M"],
  "includePhotoDetails": true,
  "includePhotographerStats": true
}
```

#### Example: photos related to a seed photo

```json
{
  "mode": "byRelated",
  "photoIds": ["1Z2niiBPg5A"],
  "orientation": "landscape",
  "maxItems": 40
}
```

### Use cases

- **Content and design teams** — source on-brand photography in the right orientation and colour.
- **ML and computer-vision datasets** — build labelled image sets with captions, tags, EXIF, dimensions, and geodata.
- **Photographer research** — track a portfolio's newest work, popular shots, and lifetime reach.
- **Visual discovery** — expand a seed photo into Unsplash's related-photo feed.
- **Collection monitoring** — keep an audit trail of a curated collection or topic over time.
- **Wallpaper and app catalogs** — pull multiple resolutions and BlurHash placeholders in one run.
- **Market and trend analysis** — compare likes, views, and downloads across subjects and visual styles.

### Data source and licence

This actor reads Unsplash's public website data. It is not affiliated with, endorsed by, or sponsored by Unsplash.

Photos are covered by the [Unsplash License](https://unsplash.com/license): free for commercial and non-commercial use, no permission needed, and **attribution is appreciated but not required**. You may not sell unmodified photos or compile Unsplash images to replicate a competing image service. Unsplash+ photos are covered by the separate Unsplash+ License and need an Unsplash+ plan for full-size files; their CDN images are watermarked.

Unsplash's API guidelines ask apps to trigger `links.download_location` when a user downloads a photo. That endpoint requires Unsplash API credentials. This actor is an independent website scraper: it returns `downloadUrl` (a direct CDN file) and `downloadPageUrl` (Unsplash's tracked web download page) instead of calling `download_location`. If you build a product on the official API, follow its guidelines and use your own credentials.

### FAQ

**Do I need an Unsplash API key or account?**
No. All modes work without an account, API key, login, or cookies.

**How many photos can I get, and can I pass the 4,000-per-query search limit?**
`maxItems` caps a run at 1,000 photos. A single keyword search has its own 4,000-row window. To go wider, partition the query: run it separately for landscape / portrait / squarish, for different dominant colours, or for free vs Unsplash+ photos, then combine the rows by `id`. Topic, collection, photographer, and related feeds can be deeper still.

**Why are `views` and `downloads` missing on some rows?**
Unsplash publishes per-photo stats for free photos only. Premium photo stats are suppressed, so the actor omits them instead of returning a misleading zero. Photographer lifetime totals are still available when enabled.

**Why are EXIF, location, or tags missing?**
They are photographer-supplied and only present on some photos. The detail pass adds them when Unsplash has them; otherwise the field is omitted.

**Why is there no family-friendly flag?**
Unsplash's JSON source does not expose it. The flag only appears in the HTML photo page (300-380 KB decompressed, ~70 KB compressed), and a Range request for the JSON-LD window returns HTTP 416, so the actor does not pay an extra full-page read per photo for one low-value boolean.

**Does the actor return illustrations?**
No. This is a photo actor. Unsplash serves illustrations as a separate asset type; mixing them would change the record schema and the supported filters.

**What is the difference between `downloadUrl`, `downloadPageUrl`, and `sourceUrl`? Does the actor download files?**
`downloadUrl` and the `imageUrls` variants are direct CDN files that work from scripts and integrations. `downloadPageUrl` and `sourceUrl` are Unsplash web pages protected by its bot check; they open normally in a browser. The actor returns metadata and URLs, not image binaries — use your own downloader if you need the files.

**Can I use the photos commercially?**
Yes, under the Unsplash License, including commercial use. Attribution is appreciated but not required. Do not sell unmodified photos or build a competing image service from Unsplash content. Check the licence for every use case.

**Why does a portfolio or topic stop before `maxItems`?**
The source may have fewer photos than requested, or a filter (orientation/free-only) removed candidates. The actor only counts photos that pass every filter, so it never returns short by silently including filtered-out rows.

**Do I need to configure a proxy? How fresh is the data?**
No proxy setup is needed; the actor chooses a working connection automatically. Every run reads Unsplash live, with nothing cached between runs.

# Actor input Schema

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

What to scrape. Each mode has its own list field below.

## `searchQueries` (type: `array`):

One or more keywords, e.g. `nature`, `coffee shop`, `minimal workspace`. Each query is searched separately.

## `topicSlugs` (type: `array`):

One or more curated Unsplash photo topics.

## `collectionIds` (type: `array`):

Collection IDs or collection URLs, e.g. `1065396` or `https://unsplash.com/collections/1065396/...`.

## `usernames` (type: `array`):

Photographer usernames with or without `@`, or profile URLs, e.g. `v2osk` or `https://unsplash.com/@v2osk`.

## `photoIds` (type: `array`):

Unsplash photo IDs, slugs, or photo page URLs, e.g. `fWBZ9r4vO9M`. Used as lookup targets in byPhoto and as seed photos in byRelated.

## `startUrls` (type: `array`):

Any mix of supported Unsplash search, topic, collection, photographer, or photo URLs.

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

Only keep photos of this orientation. The filter is exact and works in every mode.

## `color` (type: `string`):

Filter keyword searches by Unsplash's dominant-colour tag. Not supported on topic, collection, or portfolio feeds.

## `freeOnly` (type: `boolean`):

Exclude Unsplash+ (paid/locked) photos. Their full-size files require a separate Unsplash+ plan.

## `searchOrderBy` (type: `string`):

Sort keyword search results by best match or newest first.

## `userOrderBy` (type: `string`):

Sort a photographer's photos. Pinned matches the order shown on their Unsplash profile.

## `includePhotoDetails` (type: `boolean`):

Add tags, camera EXIF, geolocation, topics, and view/download counts. Costs extra requests per photo and is slower.

## `includePhotographerStats` (type: `boolean`):

Add each photographer's lifetime view and download totals. One extra request per photographer.

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

Hard cap on emitted photos across all queries and URLs.

## Actor input object example

```json
{
  "mode": "search",
  "searchQueries": [
    "nature"
  ],
  "topicSlugs": [],
  "collectionIds": [],
  "usernames": [],
  "photoIds": [],
  "startUrls": [],
  "orientation": "any",
  "color": "any",
  "freeOnly": false,
  "searchOrderBy": "relevant",
  "userOrderBy": "pinned",
  "includePhotoDetails": false,
  "includePhotographerStats": false,
  "maxItems": 2
}
```

# Actor output Schema

## `photos` (type: `string`):

Dataset containing all scraped Unsplash photos.

# 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": "search",
    "searchQueries": [
        "nature"
    ],
    "topicSlugs": [],
    "collectionIds": [],
    "usernames": [],
    "photoIds": [],
    "startUrls": [],
    "orientation": "any",
    "color": "any",
    "freeOnly": false,
    "searchOrderBy": "relevant",
    "userOrderBy": "pinned",
    "includePhotoDetails": false,
    "includePhotographerStats": false,
    "maxItems": 2
};

// Run the Actor and wait for it to finish
const run = await client.actor("dataspryva/unsplash-photo-search-by-topic-collection").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": "search",
    "searchQueries": ["nature"],
    "topicSlugs": [],
    "collectionIds": [],
    "usernames": [],
    "photoIds": [],
    "startUrls": [],
    "orientation": "any",
    "color": "any",
    "freeOnly": False,
    "searchOrderBy": "relevant",
    "userOrderBy": "pinned",
    "includePhotoDetails": False,
    "includePhotographerStats": False,
    "maxItems": 2,
}

# Run the Actor and wait for it to finish
run = client.actor("dataspryva/unsplash-photo-search-by-topic-collection").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": "search",
  "searchQueries": [
    "nature"
  ],
  "topicSlugs": [],
  "collectionIds": [],
  "usernames": [],
  "photoIds": [],
  "startUrls": [],
  "orientation": "any",
  "color": "any",
  "freeOnly": false,
  "searchOrderBy": "relevant",
  "userOrderBy": "pinned",
  "includePhotoDetails": false,
  "includePhotographerStats": false,
  "maxItems": 2
}' |
apify call dataspryva/unsplash-photo-search-by-topic-collection --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,dataspryva/unsplash-photo-search-by-topic-collection"
        }
    }
}
```

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/hyzBjwc4TeOTrPbn3/builds/cdOIAmh17Gliaabig/openapi.json
