# How to Integrate Bolt.new with Pixabay API

- Tool: Bolt.new
- Difficulty: Intermediate
- Time required: 15 minutes
- Last updated: April 2026

## TL;DR

Integrate Pixabay's free stock media API into your Bolt.new app with just an API key — no OAuth, no credit card, no licensing fees. Search over 4.5 million Creative Commons images, videos, and illustrations. A simple query parameter API that can be called from client-side code or an API route. Get your free key at pixabay.com/api/docs and start displaying images in under 10 minutes.

## Add Free Stock Images and Videos to Your Bolt.new App with Pixabay

Pixabay is the simplest stock media API available for Bolt.new integrations. Every image and video in Pixabay's library is licensed under the Pixabay License, which allows free use for commercial and personal projects without attribution (though attribution is appreciated). The API requires just a single API key passed as a query parameter — no OAuth flow, no token exchange, no webhooks, no complex setup. It is genuinely a 10-minute integration.

The Pixabay API covers four media types: photos (realistic photography), illustrations (digital artwork), vectors (SVG-format graphics), and videos. Each type has its own search endpoint. Results include multiple image size variants (preview thumbnail, web-size, full-size), download URLs, contributor username, view and like counts, and color tags. The API returns up to 200 results per page with a maximum of 20 pages, enabling browsing of up to 4,000 results per query.

For most use cases, Pixabay's API can be called directly from client-side React code since Pixabay's servers include permissive CORS headers. However, proxying through a Next.js API route is the recommended practice: it keeps your API key out of the client bundle (preventing key theft and abuse), enables server-side caching to reduce API calls, and follows the same consistent pattern used for all other integrations in your app. The free tier allows 100 requests per minute, which is sufficient for most apps without caching.

## Before you start

- A free Pixabay API key (register at pixabay.com — instant approval, no credit card)
- A Bolt.new project (Vite or Next.js — both work with Pixabay's simple query API)
- No Supabase or external backend required for basic image search

## Step-by-step guide

### 1. Get Your Pixabay API Key

Go to pixabay.com and create a free account — registration takes under a minute and requires only an email address and username. After confirming your email, go to the API documentation at pixabay.com/api/docs/. Your API key is displayed on this page once you're logged in. Copy it. The free Pixabay API key allows 100 requests per minute and up to 2,500 images per request (with the `per_page` parameter maxed at 200). There are no daily or monthly limits on the free tier — the only constraint is the 100 requests per minute rate limit. The API key is a simple alphanumeric string passed as the `key` query parameter in every request. Test it immediately: open a new browser tab and go to `https://pixabay.com/api/?key=YOUR_KEY&q=sunset&image_type=photo` — you should see a JSON response with image data. Add the key to your .env.local file as `PIXABAY_API_KEY=your_key_here` (or `VITE_PIXABAY_API_KEY=your_key_here` if using Vite and calling client-side — though server-side is recommended).

```
// lib/pixabay.ts
const PIXABAY_BASE = 'https://pixabay.com/api';

export interface PixabayImage {
  id: number;
  pageURL: string;
  type: string;
  tags: string;
  previewURL: string;
  previewWidth: number;
  previewHeight: number;
  webformatURL: string;
  webformatWidth: number;
  webformatHeight: number;
  largeImageURL: string;
  imageWidth: number;
  imageHeight: number;
  downloads: number;
  likes: number;
  views: number;
  user: string;
  userImageURL: string;
}

export interface PixabayVideo {
  id: number;
  pageURL: string;
  tags: string;
  duration: number;
  picture_id: string;
  user: string;
  userImageURL: string;
  views: number;
  downloads: number;
  likes: number;
  videos: {
    tiny: { url: string; width: number; height: number; size: number };
    small: { url: string; width: number; height: number; size: number };
    medium: { url: string; width: number; height: number; size: number };
    large: { url: string; width: number; height: number; size: number };
  };
}

export interface PixabaySearchOptions {
  imageType?: 'all' | 'photo' | 'illustration' | 'vector';
  orientation?: 'all' | 'horizontal' | 'vertical';
  category?: string;
  colors?: string;
  order?: 'popular' | 'latest';
  page?: number;
  perPage?: number;
  safeSearch?: boolean;
}

export async function searchImages(
  query: string,
  options: PixabaySearchOptions = {}
): Promise<{ hits: PixabayImage[]; total: number; totalHits: number }> {
  const params = new URLSearchParams({
    key: process.env.PIXABAY_API_KEY!,
    q: query,
    image_type: options.imageType ?? 'photo',
    orientation: options.orientation ?? 'all',
    order: options.order ?? 'popular',
    page: String(options.page ?? 1),
    per_page: String(Math.min(options.perPage ?? 20, 200)),
    safesearch: options.safeSearch !== false ? 'true' : 'false',
  });

  if (options.category) params.set('category', options.category);
  if (options.colors) params.set('colors', options.colors);

  const res = await fetch(`${PIXABAY_BASE}/?${params}`);
  if (!res.ok) throw new Error(`Pixabay API error: ${res.status}`);
  return res.json();
}

export async function searchVideos(
  query: string,
  options: { order?: 'popular' | 'latest'; page?: number; perPage?: number } = {}
): Promise<{ hits: PixabayVideo[]; total: number; totalHits: number }> {
  const params = new URLSearchParams({
    key: process.env.PIXABAY_API_KEY!,
    q: query,
    order: options.order ?? 'popular',
    page: String(options.page ?? 1),
    per_page: String(Math.min(options.perPage ?? 20, 200)),
  });

  const res = await fetch(`${PIXABAY_BASE}/videos/?${params}`);
  if (!res.ok) throw new Error(`Pixabay Videos API error: ${res.status}`);
  return res.json();
}
```

**Expected result:** The Pixabay utility is ready. Calling searchImages('sunset') from a server-side API route returns image data with preview and download URLs.

### 2. Create the Search API Route

Even though Pixabay allows browser-side calls, create a Next.js API route to proxy requests. This keeps your API key out of the client bundle (where it could be extracted by anyone viewing page source), allows server-side caching to reduce API calls and stay within rate limits, and provides a consistent pattern with your other API integrations. The route accepts `q` (query), `type` (photo/illustration/vector/all), `orientation`, `page`, and `order` parameters. For image search, call `/api/pixabay/images`. For video search, call `/api/pixabay/videos`. In Bolt's WebContainer preview, this outbound HTTPS call to Pixabay's API works without any issues — it's a standard HTTP request, and Bolt's runtime handles outbound connections just fine. You can build and test the full image gallery in the preview before deploying.

```
// app/api/pixabay/images/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { searchImages } from '@/lib/pixabay';

export async function GET(request: NextRequest) {
  const params = request.nextUrl.searchParams;
  const query = params.get('q') ?? '';

  if (!query.trim()) {
    return NextResponse.json({ hits: [], total: 0, totalHits: 0 });
  }

  try {
    const results = await searchImages(query, {
      imageType: (params.get('type') as 'photo' | 'illustration' | 'vector') ?? 'photo',
      orientation: (params.get('orientation') as 'horizontal' | 'vertical' | 'all') ?? 'all',
      order: (params.get('order') as 'popular' | 'latest') ?? 'popular',
      page: parseInt(params.get('page') ?? '1', 10),
      perPage: parseInt(params.get('per_page') ?? '20', 10),
      safeSearch: true,
    });

    // Cache for 5 minutes
    return NextResponse.json(results, {
      headers: { 'Cache-Control': 'public, s-maxage=300, stale-while-revalidate=60' },
    });
  } catch (error) {
    console.error('Pixabay search error:', error);
    return NextResponse.json({ error: 'Image search failed' }, { status: 500 });
  }
}
```

**Expected result:** GET /api/pixabay/images?q=mountain returns Pixabay image results including preview URLs, tags, and contributor names.

### 3. Build the Image Gallery Component

Create a responsive image gallery that searches Pixabay and displays results in a grid. The gallery needs a search input with debouncing (wait 400ms after the user stops typing before firing the API call), a loading skeleton while results fetch, a responsive CSS Grid layout that shows 2 columns on mobile and 4 columns on desktop, and lazy-loaded images using the `loading='lazy'` attribute for performance. Each image card should show the Pixabay `webformatURL` (medium-size image, suitable for gallery display), the contributor's username, view/like counts, and a download button. The `largeImageURL` is the full-size download version. Clicking an image should open a lightbox or modal with the larger version and metadata. Add a filter row above the grid: image type tabs (Photos / Illustrations / Vectors), orientation toggle (All / Horizontal / Vertical), and sort order (Popular / Latest). Pagination loads the next page of results when the user scrolls to the bottom or clicks 'Load More'.

**Expected result:** A fully functional image gallery works in Bolt's WebContainer preview — search, filter, paginate, and view images in a lightbox, all with live Pixabay data.

### 4. Handle Downloads and Attribution

Pixabay's license allows free commercial and personal use of all images without mandatory attribution. However, attribution is strongly encouraged as best practice and is appreciated by contributors. Add an attribution line below each image: 'Photo by {user} on Pixabay' with a link to the original image's `pageURL`. For downloads, Pixabay provides direct download URLs for different sizes. The `largeImageURL` is the full-resolution JPEG. Note that the `largeImageURL` and `imageURL` fields require an API call to a specific endpoint to get the actual full-resolution URL — the search results only include up to the large preview. To enable direct downloads, link the download button to the `webformatURL` (which is freely downloadable) or the `largeImageURL`. For video downloads, use the `videos.large.url` or `videos.medium.url` from the video search results. Implement the download as a fetch-then-blob approach to trigger the browser's download dialog, since direct link navigation may just open the image in a new tab instead of downloading.

```
// utils/download.ts
export async function downloadMedia(url: string, filename: string): Promise<void> {
  try {
    const response = await fetch(url);
    const blob = await response.blob();
    const objectUrl = URL.createObjectURL(blob);
    
    const link = document.createElement('a');
    link.href = objectUrl;
    link.download = filename;
    document.body.appendChild(link);
    link.click();
    document.body.removeChild(link);
    URL.revokeObjectURL(objectUrl);
  } catch (error) {
    console.error('Download failed:', error);
    // Fallback: open in new tab
    window.open(url, '_blank');
  }
}

// Usage in component:
// downloadMedia(image.largeImageURL, `pixabay-${image.id}.jpg`);
```

**Expected result:** Users can download images directly from the gallery. Clicking Download triggers a file download rather than opening the image in a new tab. Attribution shows below each image.

## Best practices

- Store your Pixabay API key in environment variables and access it server-side — even though Pixabay allows client-side calls, keeping the key server-side prevents abuse and key theft
- Add debouncing (400ms) to search inputs to avoid exhausting the 100 requests per minute rate limit during fast typing
- Cache API route responses with Cache-Control headers — Pixabay's popular results change infrequently and caching significantly reduces API usage
- Always set safesearch=true in production unless your platform explicitly caters to adult content
- Include 'Photo by {user} on Pixabay' attribution in your UI as best practice — while not legally required, it supports the community of contributors
- Use webformatURL for gallery thumbnails and largeImageURL for full-size modal views — don't load large images in grid thumbnails
- For the download action, use fetch-then-blob to trigger the browser's download dialog rather than direct link navigation, which may open the image in a new tab

## Use cases

### Image Picker for Content Creation Tools

Embed a Pixabay image search inside a blog editor, email builder, or social media post creator. Users can search for relevant free images and insert them directly into their content without leaving the app.

Prompt example:

```
Add a Pixabay image picker to my content editor. Create a modal with a search input and a grid of Pixabay photo results. Users can type a search term, browse results, and click an image to insert its URL into the editor. Show image tags, contributor name, and dimensions on hover. Use the Pixabay API with my API key stored in environment variables.
```

### Background Image Selector

Let users customize their profile, dashboard, or presentation background by choosing from Pixabay's curated image collection. Search by color, category, or keyword. Preview images before selecting.

Prompt example:

```
Build a background image selector that searches Pixabay for high-resolution landscape and abstract images. Show a grid of large previews. When the user selects an image, set it as their profile background and save the Pixabay image URL to Supabase. Allow searching by keyword and filtering by orientation (horizontal/vertical) and color.
```

### Stock Video Browser for Video Projects

A video search interface that lets users find free Pixabay videos by keyword and duration. Show video previews with play-on-hover and download links. Filter by resolution (HD, 4K) and duration.

Prompt example:

```
Build a free stock video browser using the Pixabay Videos API. Search for videos by keyword, show results in a grid with animated preview on hover, display resolution badge (HD/4K) and duration. When a user clicks a video, show a modal with the full preview and download button links for SD, HD, and 4K versions. Use /api/pixabay/videos as the server-side proxy route.
```

## Troubleshooting

### Pixabay API returns 400 Bad Request for search queries

Cause: The API key is missing, malformed, or the query parameter has special characters that are not URL-encoded.

Solution: Verify PIXABAY_API_KEY is set in .env.local. Use encodeURIComponent(query) or URLSearchParams to properly encode the search query. Confirm the key is passed as the 'key' parameter (not 'api_key' or 'apiKey').

```
const params = new URLSearchParams({
  key: process.env.PIXABAY_API_KEY!,
  q: query, // URLSearchParams auto-encodes
  per_page: '20',
});
```

### Images return 403 Forbidden when loading in the browser

Cause: The direct `largeImageURL` may have download restrictions. The `webformatURL` is intended for embedding; `largeImageURL` may require authentication for direct access.

Solution: Use `webformatURL` for gallery display and thumbnails — it is always accessible for embedding. For the download action, use the fetch-then-blob approach in utils/download.ts rather than direct image links. The `previewURL` (thumbnail) and `webformatURL` are always embeddable.

### Rate limit errors (429 Too Many Requests) during development

Cause: The free Pixabay API tier allows 100 requests per minute. Fast typing in the search input without debouncing can exhaust the limit quickly.

Solution: Add a 400ms debounce to the search input. Add Cache-Control headers to your API route to cache responses. The cache means repeated searches for the same query don't consume additional API calls.

```
// Add debounce to search input
const [debouncedQuery, setDebouncedQuery] = useState(query);
useEffect(() => {
  const timer = setTimeout(() => setDebouncedQuery(query), 400);
  return () => clearTimeout(timer);
}, [query]);
```

### Video previews don't play in the gallery

Cause: Pixabay video URLs use `.mp4` format but browser autoplay policies block unmuted videos from autoplaying.

Solution: Add muted and playsInline attributes to video elements for hover-preview. Only trigger play() on mouseenter events. For the full preview modal, allow user-initiated play with sound.

```
<video
  src={video.videos.tiny.url}
  muted
  playsInline
  loop
  onMouseEnter={(e) => (e.target as HTMLVideoElement).play()}
  onMouseLeave={(e) => (e.target as HTMLVideoElement).pause()}
/>
```

## Frequently asked questions

### Is Pixabay API free to use in commercial projects?

Yes. The Pixabay API is free to use and the images are licensed under the Pixabay License, which allows free commercial and non-commercial use without mandatory attribution. The API key is free with no monthly or per-request fees — only rate limits (100 requests/minute) apply.

### Can I call the Pixabay API directly from client-side React in Bolt?

Technically yes — Pixabay's servers include CORS headers that allow browser-side calls. However, routing through a Next.js API route is strongly recommended to keep your API key out of the client bundle, enable server-side caching, and maintain consistent integration patterns. Your API key in client code can be extracted by anyone viewing page source.

### Does the Pixabay API work in Bolt's WebContainer preview?

Yes, completely. Pixabay's API is HTTP-based with no native modules or TCP sockets required. Outbound HTTPS calls work in Bolt's WebContainer, so you can build and test the full image gallery in the preview before deploying. There are no webhook requirements for Pixabay.

### How many images can I get per API request?

The Pixabay API returns up to 200 images per request (set per_page=200). Total search results are capped at the totalHits value returned in the response. You can paginate through results using the page parameter, giving access to up to 4,000 results per query (200 per page × 20 pages maximum).

### How do I deploy a Bolt.new app with Pixabay to Netlify?

Connect Netlify in Bolt Settings → Applications and click Publish. Add PIXABAY_API_KEY to Netlify's environment variables in Site Configuration → Environment Variables. Redeploy to apply. No webhook registration or OAuth configuration is needed — Pixabay's simple API key setup requires only this one environment variable.

---

Source: https://www.rapidevelopers.com/bolt-ai-integrations/pixabay-api
© RapidDev — https://www.rapidevelopers.com/bolt-ai-integrations/pixabay-api
