Integrate Unsplash API with WordPress Using WP-CLI and Bash

Finding high-quality, royalty-free images for every blog post is time-consuming. Manually searching Unsplash, downloading images, uploading to WordPress, and adding attribution takes 5-10 minutes per post. For content-heavy sites publishing daily, that’s hours of wasted effort every week.

What if you could automatically fetch relevant images from Unsplash and set them as featured images—all from the command line?

In this tutorial, you’ll learn to integrate the Unsplash API with WordPress using WP-CLI and Bash. You’ll build a complete automation system that searches for images, downloads them, imports to WordPress media library, sets featured images, and handles proper attribution automatically.

This is a key component of the automated blog publishing pipeline you’ll build in WPCLI Mastery.

Why Automate Images with Unsplash?

The Manual Image Problem

Typical workflow:

  1. Search Unsplash for relevant image (2 min)
  2. Download image (30 sec)
  3. Upload to WordPress (1 min)
  4. Set as featured image (30 sec)
  5. Add attribution in caption/alt text (1 min)

Total: ~5 minutes per post

For 20 posts/month = 100 minutes wasted

Automated Solution

With WP-CLI + Unsplash API:

# One command, 5 seconds
wp unsplash set-featured 123 --query="technology"

Result:

  • Image downloaded
  • Imported to media library
  • Set as featured image
  • Attribution added
  • Alt text optimized

Unsplash Benefits

High Quality: Professional photography, all free Huge Library: Over 3 million images Legal Safety: Completely free for commercial use Developer-Friendly: Well-documented API SEO Value: Proper attribution builds credibility

According to Unsplash, images are downloaded over 10 billion times per year. It’s the go-to source for professional content creators.

Prerequisites

Before integrating Unsplash API, ensure you have:

Required Software

  • WP-CLI installed – Installation guide
  • curl – For API requests
  • jq – JSON parsing tool
  • Bash – Shell scripting knowledge

Install missing tools:

# Ubuntu/Debian
sudo apt-get install curl jq

# macOS
brew install curl jq

WordPress Requirements

  • WordPress site with WP-CLI access
  • Write permissions for uploads directory
  • Basic understanding of WordPress media library

Bash Knowledge

Helpful background:

Getting Unsplash API Access

Step 1: Create Unsplash Account

  1. Visit Unsplash.com
  2. Sign up for free account
  3. Verify your email

Step 2: Register Your Application

  1. Go to Unsplash Developers
  2. Click “Register as a Developer”
  3. Accept the API terms
  4. Click “New Application”

Step 3: Create API Application

Fill in application details:

  • Application name: “WordPress Blog Automation”
  • Description: “Automated featured image fetching for WordPress blog”
  • Callback URLs: (not needed for this use case)

Important: Accept the API Guidelines

Step 4: Get API Keys

After creating your app, you’ll receive:

  • Access Key: Public key for API requests (Demo: Limited to 50 requests/hour)
  • Secret Key: Keep private (not needed for basic usage)

Demo vs Production:

  • Demo: 50 requests/hour (good for testing)
  • Production: 5,000 requests/hour (requires app approval by Unsplash)

For most blogs, demo limits are sufficient. Production approval takes 1-2 business days.

Step 5: Store API Key Securely

Never hardcode API keys in scripts.

Store in environment variable:

# Add to ~/.bashrc or ~/.bash_profile
export UNSPLASH_ACCESS_KEY="your_access_key_here"

# Reload
source ~/.bashrc

Or use a config file:

# Create config file
nano ~/.unsplash-config

# Add key
UNSPLASH_ACCESS_KEY=your_access_key_here

# Secure it
chmod 600 ~/.unsplash-config

Understanding Unsplash API

API Endpoints

Search Photos:

GET https://api.unsplash.com/search/photos

Get Random Photo:

GET https://api.unsplash.com/photos/random

Get Specific Photo:

GET https://api.unsplash.com/photos/{id}

Basic API Request

curl -H "Authorization: Client-ID YOUR_ACCESS_KEY" \
	"https://api.unsplash.com/photos/random?query=nature"

Response Format

Unsplash returns JSON:

{
	"id": "abc123",
	"urls": {
		"raw": "https://images.unsplash.com/photo-...",
		"full": "https://images.unsplash.com/photo-...",
		"regular": "https://images.unsplash.com/photo-...",
		"small": "https://images.unsplash.com/photo-..."
	},
	"user": {
		"name": "John Doe",
		"username": "johndoe",
		"links": {
			"html": "https://unsplash.com/@johndoe"
		}
	},
	"description": "Beautiful sunset over mountains",
	"alt_description": "orange sunset"
}

Key fields:

  • urls.regular: Best for WordPress featured images (1080px wide)
  • user.name: For attribution
  • user.links.html: Link to photographer’s profile
  • alt_description: Good for alt text

Read full docs: Unsplash API Documentation

Fetching Images with curl

Search for Images

#!/bin/bash

UNSPLASH_KEY="your_access_key"
QUERY="technology"

# Search for images
response=$(curl -s -H "Authorization: Client-ID ${UNSPLASH_KEY}" \
	"https://api.unsplash.com/search/photos?query=${QUERY}&per_page=1")

# Parse response with jq
image_url=$(echo "$response" | jq -r '.results[0].urls.regular')
photo_id=$(echo "$response" | jq -r '.results[0].id')
photographer=$(echo "$response" | jq -r '.results[0].user.name')
photographer_url=$(echo "$response" | jq -r '.results[0].user.links.html')
alt_text=$(echo "$response" | jq -r '.results[0].alt_description')

echo "Image URL: ${image_url}"
echo "Photographer: ${photographer}"
echo "Alt Text: ${alt_text}"

Get Random Image

get_random_image() {
		local query=$1
		local orientation=${2:-landscape}  # landscape, portrait, squarish

		local response=$(curl -s -H "Authorization: Client-ID ${UNSPLASH_KEY}" \
			"https://api.unsplash.com/photos/random?query=${query}&orientation=${orientation}")

		echo "$response"
}

# Usage
response=$(get_random_image "coding" "landscape")
image_url=$(echo "$response" | jq -r '.urls.regular')

Search Parameters

Common parameters:

# Search with pagination
?query=nature&page=2&per_page=10

# Filter by orientation
?query=laptop&orientation=landscape

# Filter by color
?query=sunset&color=orange

# Combine filters
?query=workspace&orientation=landscape&color=black_and_white

Orientation options:

  • landscape – Horizontal (best for featured images)
  • portrait – Vertical
  • squarish – Square-ish

Color options:

  • black_and_whiteblackwhiteyelloworangeredpurplemagentagreentealblue

Downloading Images

Download Image to Filesystem

download_image() {
		local image_url=$1
		local output_file=$2

		if curl -s -o "$output_file" "$image_url"; then
				echo "Downloaded: ${output_file}"
				return 0
		else
				echo "Error: Download failed" >&2
				return 1
		fi
}

# Usage
image_url="https://images.unsplash.com/photo-xyz?w=1080"
output="/tmp/unsplash-image.jpg"

download_image "$image_url" "$output"

Generate Unique Filename

generate_filename() {
		local query=$1
		local timestamp=$(date +%Y%m%d_%H%M%S)
		local random=$(head /dev/urandom | tr -dc A-Za-z0-9 | head -c 6)

		# Sanitize query (remove spaces, special chars)
		local clean_query=$(echo "$query" | tr ' ' '-' | tr -cd '[:alnum:]-')

		echo "unsplash-${clean_query}-${timestamp}-${random}.jpg"
}

# Usage
filename=$(generate_filename "mountain landscape")
# Output: unsplash-mountain-landscape-20250325_143022-aB3xY9.jpg

Complete Download Function

fetch_and_download() {
		local query=$1
		local output_dir=${2:-/tmp}

		# Fetch image data
		local response=$(curl -s -H "Authorization: Client-ID ${UNSPLASH_KEY}" \
			"https://api.unsplash.com/photos/random?query=${query}&orientation=landscape")

		# Parse response
		local image_url=$(echo "$response" | jq -r '.urls.regular')
		local photo_id=$(echo "$response" | jq -r '.id')

		if [ "$image_url" = "null" ] || [ -z "$image_url" ]; then
				echo "Error: No image found for query '${query}'" >&2
				return 1
		fi

		# Generate filename
		local filename=$(generate_filename "$query")
		local filepath="${output_dir}/${filename}"

		# Download image
		if curl -s -o "$filepath" "$image_url"; then
				echo "$filepath"
				return 0
		else
				echo "Error: Download failed" >&2
				return 1
		fi
}

# Usage
image_file=$(fetch_and_download "workspace" "/tmp")
echo "Image saved: ${image_file}"

Importing to WordPress Media Library

WP-CLI Media Import

WP-CLI provides wp media import for adding images to media library.

Basic import:

wp media import /tmp/image.jpg --title="My Image"

With metadata:

wp media import /tmp/image.jpg \
	--title="Mountain Landscape" \
	--alt="Beautiful mountain sunset" \
	--caption="Photo by John Doe on Unsplash" \
	--porcelain  # Returns attachment ID only

Import Function with Metadata

import_to_wordpress() {
		local image_file=$1
		local title=$2
		local alt_text=$3
		local caption=$4

		# Import and get attachment ID
		local attachment_id=$(wp media import "$image_file" \
				--title="$title" \
				--alt="$alt_text" \
				--caption="$caption" \
				--porcelain)

		if [ -n "$attachment_id" ]; then
				echo "Imported as attachment ID: ${attachment_id}"
				echo "$attachment_id"
				return 0
		else
				echo "Error: Import failed" >&2
				return 1
		fi
}

# Usage
attachment_id=$(import_to_wordpress \
		"/tmp/image.jpg" \
		"Tech Workspace" \
		"Modern laptop on wooden desk" \
		"Photo by Jane Smith on Unsplash")

Complete Fetch and Import

fetch_and_import() {
		local query=$1

		# Fetch image from Unsplash
		local response=$(curl -s -H "Authorization: Client-ID ${UNSPLASH_KEY}" \
			"https://api.unsplash.com/photos/random?query=${query}&orientation=landscape")

		# Parse metadata
		local image_url=$(echo "$response" | jq -r '.urls.regular')
		local photographer=$(echo "$response" | jq -r '.user.name')
		local photographer_url=$(echo "$response" | jq -r '.user.links.html')
		local alt_text=$(echo "$response" | jq -r '.alt_description')
		local description=$(echo "$response" | jq -r '.description // .alt_description')

		# Download to temp file
		local temp_file="/tmp/unsplash-$(date +%s).jpg"
		curl -s -o "$temp_file" "$image_url"

		# Prepare metadata
		local title="$description"
		local caption="Photo by ${photographer} on Unsplash (${photographer_url})"

		# Import to WordPress
		local attachment_id=$(wp media import "$temp_file" \
				--title="$title" \
				--alt="$alt_text" \
				--caption="$caption" \
				--porcelain)

		# Clean up temp file
		rm -f "$temp_file"

		echo "$attachment_id"
}

# Usage
attachment_id=$(fetch_and_import "coffee shop")
echo "Imported image: ${attachment_id}"

Set Featured Image for Post

wp post meta update POST_ID _thumbnail_id ATTACHMENT_ID

Example:

# Post ID 123, Attachment ID 456
wp post meta update 123 _thumbnail_id 456

Complete Function

set_featured_image() {
		local post_id=$1
		local attachment_id=$2

		if wp post meta update "$post_id" _thumbnail_id "$attachment_id" &>/dev/null; then
				echo "Featured image set for post ${post_id}"
				return 0
		else
				echo "Error: Failed to set featured image" >&2
				return 1
		fi
}

# Usage
set_featured_image 123 456

One-Command Solution

Combine everything:

set_unsplash_featured() {
		local post_id=$1
		local query=$2

		echo "Fetching image for: ${query}"

		# Fetch and import
		local attachment_id=$(fetch_and_import "$query")

		if [ -z "$attachment_id" ]; then
				echo "Error: Failed to fetch/import image" >&2
				return 1
		fi

		# Set as featured
		if set_featured_image "$post_id" "$attachment_id"; then
				echo "✓ Featured image set successfully"
				echo "Post ID: ${post_id}"
				echo "Attachment ID: ${attachment_id}"
				return 0
		else
				return 1
		fi
}

# Usage
set_unsplash_featured 123 "technology workspace"

Handling Attribution

Unsplash Attribution Requirements

Per Unsplash License:

Required:

  • Credit photographer by name
  • Link to photographer’s Unsplash profile

Good attribution:

“Photo by Photographer Name on Unsplash”

Bad attribution:

“From Unsplash” (missing photographer credit)

Add Attribution to Caption

create_attribution() {
		local photographer=$1
		local photographer_url=$2

		echo "Photo by ${photographer} on Unsplash (${photographer_url})"
}

# Usage
photographer="John Doe"
url="https://unsplash.com/@johndoe"
caption=$(create_attribution "$photographer" "$url")
# Output: Photo by John Doe on Unsplash (https://unsplash.com/@johndoe)

Add Attribution to Post Content

Optionally add attribution directly in post content:

add_attribution_to_post() {
		local post_id=$1
		local photographer=$2
		local photographer_url=$3

		# Get current content
		local content=$(wp post get "$post_id" --field=content)

		# Append attribution
		local attribution="<p><em>Featured image by <a href=\"${photographer_url}\">${photographer}</a> on <a href=\"https://unsplash.com\">Unsplash</a></em></p>"

		local new_content="${content}\n\n${attribution}"

		# Update post
		wp post update "$post_id" --post_content="$new_content"
}

Trigger Download Tracking

Unsplash requires triggering download endpoint for analytics:

trigger_download() {
		local download_url=$1

		curl -s -H "Authorization: Client-ID ${UNSPLASH_KEY}" "$download_url" > /dev/null
}

# Get download URL from API response
download_url=$(echo "$response" | jq -r '.links.download_location')
trigger_download "$download_url"

Important: This is required by Unsplash API guidelines.

Complete Automation Script

Here’s the full production-ready script:

#!/bin/bash
#
# Unsplash WordPress Integration Script
# Fetches images from Unsplash and sets as WordPress featured images
#

set -euo pipefail

#############################################
# CONFIGURATION
#############################################

# Load API key from config file
if [ -f ~/.unsplash-config ]; then
		source ~/.unsplash-config
fi

UNSPLASH_KEY="${UNSPLASH_ACCESS_KEY:-}"
TEMP_DIR="/tmp/unsplash-wp"

#############################################
# FUNCTIONS
#############################################

check_dependencies() {
		local missing=()

		command -v curl &>/dev/null || missing+=("curl")
		command -v jq &>/dev/null || missing+=("jq")
		command -v wp &>/dev/null || missing+=("wp-cli")

		if [  then
				echo "Error: Missing dependencies: ${missing[*]}" >&2
				exit 1
		fi

		if [ -z "$UNSPLASH_KEY" ]; then
				echo "Error: UNSPLASH_ACCESS_KEY not set" >&2
				echo "Set it in ~/.unsplash-config or environment" >&2
				exit 1
		fi
}

fetch_unsplash_image() {
		local query=$1
		local orientation=${2:-landscape}

		local url="https://api.unsplash.com/photos/random"
		url="${url}?query=${query}&orientation=${orientation}"

		local response=$(curl -s -H "Authorization: Client-ID ${UNSPLASH_KEY}" "$url")

		# Check for errors
		local errors=$(echo "$response" | jq -r '.errors // empty')
		if [ -n "$errors" ]; then
				echo "API Error: $errors" >&2
				return 1
		fi

		echo "$response"
}

download_image() {
		local image_url=$1
		local output_file=$2

		if curl -s -L -o "$output_file" "$image_url"; then
				return 0
		else
				return 1
		fi
}

import_to_media_library() {
		local image_file=$1
		local metadata=$2

		local title=$(echo "$metadata" | jq -r '.title')
		local alt=$(echo "$metadata" | jq -r '.alt')
		local caption=$(echo "$metadata" | jq -r '.caption')

		local attachment_id=$(wp media import "$image_file" \
				--title="$title" \
				--alt="$alt" \
				--caption="$caption" \
				--porcelain 2>/dev/null)

		echo "$attachment_id"
}

set_featured_image_for_post() {
		local post_id=$1
		local attachment_id=$2

		wp post meta update "$post_id" _thumbnail_id "$attachment_id" &>/dev/null
}

trigger_download_endpoint() {
		local download_url=$1

		curl -s -H "Authorization: Client-ID ${UNSPLASH_KEY}" "$download_url" > /dev/null
}

#############################################
# MAIN FUNCTION
#############################################

set_unsplash_featured() {
		local post_id=$1
		local query=$2
		local orientation=${3:-landscape}

		echo "================================================"
		echo "Unsplash Featured Image Automation"
		echo "================================================"
		echo "Post ID: ${post_id}"
		echo "Query: ${query}"
		echo "Orientation: ${orientation}"
		echo "================================================"

		# Verify post exists
		if ! wp post get "$post_id" &>/dev/null; then
				echo "Error: Post ${post_id} not found" >&2
				return 1
		fi

		# Fetch image from Unsplash
		echo "Fetching image from Unsplash..."
		local response=$(fetch_unsplash_image "$query" "$orientation")

		if [ $? -ne 0 ]; then
				echo "Error: Failed to fetch image" >&2
				return 1
		fi

		# Parse response
		local image_url=$(echo "$response" | jq -r '.urls.regular')
		local photographer=$(echo "$response" | jq -r '.user.name')
		local photographer_url=$(echo "$response" | jq -r '.user.links.html')
		local alt_text=$(echo "$response" | jq -r '.alt_description // "Image from Unsplash"')
		local description=$(echo "$response" | jq -r '.description // .alt_description')
		local download_endpoint=$(echo "$response" | jq -r '.links.download_location')

		echo "Found: ${description}"
		echo "By: ${photographer}"

		# Create temp directory
		mkdir -p "$TEMP_DIR"

		# Download image
		local temp_file="${TEMP_DIR}/unsplash-$(date +%s).jpg"
		echo "Downloading image..."

		if ! download_image "$image_url" "$temp_file"; then
				echo "Error: Download failed" >&2
				return 1
		fi

		# Prepare metadata
		local caption="Photo by <a href=\"${photographer_url}\">${photographer}</a> on <a href=\"https://unsplash.com\">Unsplash</a>"

		local metadata=$(jq -n \
				--arg title "$description" \
				--arg alt "$alt_text" \
				--arg caption "$caption" \
				'{title: $title, alt: $alt, caption: $caption}')

		# Import to WordPress
		echo "Importing to media library..."
		local attachment_id=$(import_to_media_library "$temp_file" "$metadata")

		if [ -z "$attachment_id" ]; then
				echo "Error: Import failed" >&2
				rm -f "$temp_file"
				return 1
		fi

		echo "Imported as attachment ${attachment_id}"

		# Set as featured image
		echo "Setting as featured image..."
		if set_featured_image_for_post "$post_id" "$attachment_id"; then
				echo "✓ Featured image set successfully"
		else
				echo "Error: Failed to set featured image" >&2
				rm -f "$temp_file"
				return 1
		fi

		# Trigger download tracking (required by Unsplash)
		trigger_download_endpoint "$download_endpoint"

		# Clean up
		rm -f "$temp_file"

		echo "================================================"
		echo "SUCCESS!"
		echo "Post: ${post_id}"
		echo "Attachment: ${attachment_id}"
		echo "Photographer: ${photographer}"
		echo "================================================"

		return 0
}

#############################################
# SCRIPT ENTRY POINT
#############################################

# Check dependencies
check_dependencies

# Parse arguments
if [ $# -lt 2 ]; then
		echo "Usage: $0 <post_id> <search_query> [orientation]"
		echo ""
		echo "Examples:"
		echo "  $0 123 'technology workspace'"
		echo "  $0 456 'coffee shop' portrait"
		echo ""
		exit 1
fi

POST_ID=$1
QUERY=$2
ORIENTATION=${3:-landscape}

# Run main function
set_unsplash_featured "$POST_ID" "$QUERY" "$ORIENTATION"

exit $?

Usage

# Save as unsplash-wp.sh
chmod +x unsplash-wp.sh

# Set featured image for post
./unsplash-wp.sh 123 "technology workspace"

# Portrait orientation
./unsplash-wp.sh 456 "coffee shop interior" portrait

# Multiple keywords
./unsplash-wp.sh 789 "mountain sunset landscape"

Advanced Features

Process multiple posts:

# Get all posts without featured images
posts=$(wp post list --post_type=post --meta_key=_thumbnail_id --meta_compare=NOT EXISTS --field=ID)

for post_id in $posts; do
		# Get post title or category for query
		title=$(wp post get $post_id --field=title)

		# Set featured image
		./unsplash-wp.sh $post_id "$title"

		sleep 2  # Rate limiting
done

Smart Query from Post Content

Extract keywords from post:

get_query_from_post() {
		local post_id=$1

		# Get post title
		local title=$(wp post get $post_id --field=title)

		# Get first category
		local category=$(wp post term list $post_id category --field=name --format=csv | head -1)

		# Combine for better results
		echo "${category} ${title}"
}

# Usage
query=$(get_query_from_post 123)
./unsplash-wp.sh 123 "$query"

Fallback to Default Images

set_featured_with_fallback() {
		local post_id=$1
		local query=$2
		local fallback_query="abstract background"

		if ! ./unsplash-wp.sh $post_id "$query"; then
				echo "Primary query failed, trying fallback..."
				./unsplash-wp.sh $post_id "$fallback_query"
		fi
}

Error Handling

API Error Responses

Handle common errors:

handle_api_response() {
		local response=$1

		# Check for rate limit
		local rate_limit=$(echo "$response" | jq -r '.errors[0] // empty' | grep -i "rate limit")
		if [ -n "$rate_limit" ]; then
				echo "Error: API rate limit reached" >&2
				echo "Wait 1 hour or upgrade to production API" >&2
				return 1
		fi

		# Check for invalid query
		local invalid=$(echo "$response" | jq -r '.errors[0] // empty')
		if [ -n "$invalid" ]; then
				echo "Error: $invalid" >&2
				return 1
		fi

		return 0
}

Network Errors

download_with_retry() {
		local url=$1
		local output=$2
		local max_attempts=3
		local attempt=1

		while [ $attempt -le $max_attempts ]; do
				echo "Download attempt ${attempt}/${max_attempts}"

				if curl -s -L -o "$output" --connect-timeout 10 "$url"; then
						return 0
				fi

				attempt=$((attempt + 1))
				sleep 2
		done

		echo "Error: Download failed after ${max_attempts} attempts" >&2
		return 1
}

Rate Limiting

Unsplash Rate Limits

  • Demo: 50 requests/hour
  • Production: 5,000 requests/hour

Implement Rate Limiting

check_rate_limit() {
		local last_request_file="/tmp/unsplash-last-request"
		local min_interval=72  # 50 requests/hour = 72 seconds between requests

		if [ -f "$last_request_file" ]; then
				local last_request=$(cat "$last_request_file")
				local now=$(date +%s)
				local elapsed=$((now - last_request))

				if [ $elapsed -lt $min_interval ]; then
						local wait=$((min_interval - elapsed))
						echo "Rate limiting: waiting ${wait} seconds..."
						sleep $wait
				fi
		fi

		# Update last request time
		date +%s > "$last_request_file"
}

# Call before API requests
check_rate_limit

Monitor Usage

Check remaining requests:

response=$(curl -I -H "Authorization: Client-ID ${UNSPLASH_KEY}" \
	"https://api.unsplash.com/photos/random")

remaining=$(echo "$response" | grep -i "X-Ratelimit-Remaining" | cut -d: -f2 | tr -d ' \r')

echo "Remaining requests: ${remaining}"

Integration with Publishing Pipeline

Automated Blog Publishing

Combine with content automation:

publish_post_with_image() {
		local title=$1
		local content=$2
		local query=$3

		# Create post
		local post_id=$(wp post create \
				--post_title="$title" \
				--post_content="$content" \
				--post_status=publish \
				--porcelain)

		# Set featured image from Unsplash
		./unsplash-wp.sh $post_id "$query"

		echo "Published post: ${post_id}"
}

# Usage
publish_post_with_image \
		"10 Best Productivity Tools" \
		"Content here..." \
		"productivity workspace"

Integration with AI Content

# Get AI-generated content
content=$(call_ai_api "Write about productivity tools")

# Extract keywords for image
keywords=$(echo "$content" | extract_keywords)  # Custom function

# Create post
post_id=$(wp post create --post_title="..." --post_content="$content" --porcelain)

# Set Unsplash featured image
./unsplash-wp.sh $post_id "$keywords"

Learn more: Automated Blog Publishing with AI

Troubleshooting

Issue 1: “jq: command not found”

Solution:

# Ubuntu/Debian
sudo apt-get install jq

# macOS
brew install jq

# CentOS
sudo yum install jq

Issue 2: “401 Unauthorized”

Problem: Invalid API key

Solutions:

  1. Verify API key is correct
  2. Check environment variable is set:
echo $UNSPLASH_ACCESS_KEY
  1. Regenerate API key in Unsplash dashboard

Issue 3: No Images Found

Problem: Query returns no results

Solutions:

  1. Simplify query (use broader terms)
  2. Try different keywords
  3. Check query isn’t too specific
# Too specific (may fail)
query="red ferrari on mountain road at sunset"

# Better (broader)
query="sports car"

Issue 4: Rate Limit Exceeded

Problem: “Rate Limit Exceeded” error

Solutions:

  1. Wait 1 hour for demo API reset
  2. Apply for production API (5,000 req/hour)
  3. Implement rate limiting in your script

Issue 5: Import Failed

Problemwp media import fails

Solutions:

  1. Check file permissions:
ls -la /tmp/image.jpg
chmod 644 /tmp/image.jpg
  1. Verify uploads directory is writable:
wp media regenerate --dry-run
  1. Check file size (Unsplash regular = ~500KB, should be fine)

Next Steps

Congratulations! You’ve built a complete Unsplash + WordPress automation system.

Enhance Your Integration

  1. Automated Blog Publishing Pipeline – Combine with AI content
  2. Custom WP-CLI Commands – Build wp unsplash command
  3. Bash Functions – Refactor into reusable library

Build the Complete Pipeline

This Unsplash integration is one piece of a complete automated publishing system:

  1. Generate content with AI
  2. Fetch featured image from Unsplash ← You just learned this
  3. Schedule and publish post
  4. Optimize SEO automatically

Join WPCLI Mastery to build the complete system:

  • Full automated blog publishing pipeline
  • AI + Unsplash + WP-CLI integration
  • Production-ready scripts and templates
  • Early bird pricing ($99 vs $199)

Conclusion

Automating featured images with Unsplash API and WP-CLI saves hours of manual work while ensuring every post has professional, high-quality imagery. The script you built today can process thousands of posts automatically.

Key takeaways:

  • Unsplash API provides free, high-quality images
  • curl + jq makes API integration simple
  • WP-CLI media import handles WordPress integration
  • Proper attribution is required (and easy to automate)
  • Rate limiting prevents API issues
  • Integration with publishing pipelines creates powerful automation

The image automation you built today becomes even more powerful when combined with AI content generation for a complete hands-free publishing system.

Ready to implement? Start with the complete script and customize for your workflow.


Questions about Unsplash + WordPress automation? Drop a comment below!

Found this helpful? Share with WordPress developers automating content.

Next: Build a complete WordPress CI/CD pipeline with GitHub Actions that includes automated image fetching.