To integrate Replit with the Pixabay API, register a free account at pixabay.com/api/docs, get your API key, store it in Replit Secrets (lock icon π), and use Python or Node.js to search millions of free images, videos, and illustrations. No OAuth required β a single API key grants access to the full catalog under the Pixabay Content License.
Why Connect Replit to the Pixabay API?
Pixabay is one of the most accessible free media APIs available. Unlike premium stock services that require paid licenses or lengthy application approvals, Pixabay allows any registered user to access millions of royalty-free images, videos, and vector illustrations with a simple API key. This makes it ideal for prototypes, content-heavy apps, blog platforms, and tools that need to display contextual images without licensing overhead.
The Pixabay API supports searching by keyword, category, color, orientation, and media type. Results include multiple image sizes (preview, webformat, and full) along with metadata such as tags, user, views, downloads, and likes. The free tier allows up to 100 requests per 60 seconds per API key, which is sufficient for most interactive apps. Images are served directly from Pixabay's CDN, so you only need to relay the image URLs β your Replit backend handles the API call and your frontend displays the images.
Because Pixabay uses plain API key authentication, there is no OAuth token management to worry about. Store the key in Replit Secrets (lock icon π in the sidebar), read it at runtime with os.environ or process.env, and append it to your API requests. This is one of the quickest media API integrations you can build in Replit.
Integration method
You connect Replit to Pixabay by registering a free account, obtaining an API key from the Pixabay API documentation page, and storing it in Replit Secrets. Your server-side Python or Node.js code appends the key as a query parameter on every request to the Pixabay REST API. Because Pixabay uses simple API key auth with no OAuth token flow, setup takes only minutes and the same key works for images, videos, and vector illustrations.
Prerequisites
- A Replit account with a Python or Node.js project created
- A free Pixabay account at https://pixabay.com (required to access the API)
- An API key from https://pixabay.com/api/docs after logging in
- Basic familiarity with making HTTP requests in Python (requests library) or Node.js (axios or node-fetch)
Step-by-step guide
Get Your Pixabay API Key
Get Your Pixabay API Key
Go to https://pixabay.com and create a free account or log in. Once logged in, navigate to https://pixabay.com/api/docs β this page shows your personal API key in the first code example displayed at the top of the documentation. The key is a numeric string (e.g., 12345678-abcdef1234567890abcdef12). Pixabay API access is available to all registered users at no cost. The free tier allows up to 100 requests per 60 seconds per API key. For most interactive applications, this rate limit is more than adequate. If you need higher throughput, contact Pixabay to discuss increased limits for production applications. The Pixabay API does not use OAuth β the API key is the only credential you need. It acts as a query parameter called 'key' appended to every request. There is no token expiry to manage, no refresh flow, and no scopes to configure. This simplicity is one of Pixabay's biggest advantages for beginner integrations.
Pro tip: Your API key is visible immediately after logging in at pixabay.com/api/docs β you do not need to create a separate application or wait for approval.
Expected result: You have a Pixabay API key (a long alphanumeric string) ready to store in Replit.
Store the API Key in Replit Secrets
Store the API Key in Replit Secrets
Open your Replit project and click the lock icon π in the left sidebar to open the Secrets pane. Click 'Add a new secret' and enter: - Key: PIXABAY_API_KEY - Value: your Pixabay API key Click 'Add Secret' to save. Replit encrypts this value and injects it as an environment variable when your application runs. You access it in Python as os.environ['PIXABAY_API_KEY'] and in Node.js as process.env.PIXABAY_API_KEY. Even though Pixabay's API key is lower risk than payment credentials, storing it in Replit Secrets is still best practice. It prevents the key from appearing in your source code (which could be made public), and Replit's Secret Scanner will alert you if a key accidentally ends up in a committed file. Always access the key at runtime from the environment rather than hardcoding it.
Pro tip: After adding the secret, restart your Repl to ensure the environment variable is injected into the running process.
Expected result: PIXABAY_API_KEY appears in the Replit Secrets pane with the value hidden.
Search Images with Python
Search Images with Python
Install the requests library in your Replit Shell if it is not already available by running 'pip install requests'. The Pixabay API base URL is https://pixabay.com/api/ for images and illustrations, and https://pixabay.com/api/videos/ for video content. Query parameters include 'q' (search term), 'image_type' (all, photo, illustration, vector), 'orientation' (all, horizontal, vertical), 'category', 'min_width', 'min_height', 'colors', 'per_page' (3-200, default 20), and 'page'. The Flask server below exposes two endpoints: /search for image queries and /video for video queries. Both accept a 'query' parameter and return a simplified response with image URLs and metadata. The API key is read from the environment and appended server-side, so the frontend never sees it.
1import os2import requests3from flask import Flask, request, jsonify45app = Flask(__name__)67PIXABAY_API_KEY = os.environ["PIXABAY_API_KEY"]8IMAGE_API_URL = "https://pixabay.com/api/"9VIDEO_API_URL = "https://pixabay.com/api/videos/"101112def search_images(query: str, image_type: str = "all",13 orientation: str = "all", per_page: int = 20,14 page: int = 1) -> dict:15 """Search Pixabay for images, illustrations, or vectors."""16 params = {17 "key": PIXABAY_API_KEY,18 "q": query,19 "image_type": image_type, # all, photo, illustration, vector20 "orientation": orientation, # all, horizontal, vertical21 "per_page": min(max(per_page, 3), 200),22 "page": page,23 "safesearch": "true"24 }25 resp = requests.get(IMAGE_API_URL, params=params)26 resp.raise_for_status()27 return resp.json()282930def search_videos(query: str, per_page: int = 20, page: int = 1) -> dict:31 """Search Pixabay for video content."""32 params = {33 "key": PIXABAY_API_KEY,34 "q": query,35 "per_page": min(max(per_page, 3), 200),36 "page": page37 }38 resp = requests.get(VIDEO_API_URL, params=params)39 resp.raise_for_status()40 return resp.json()414243@app.route("/search")44def search():45 query = request.args.get("query", "")46 image_type = request.args.get("type", "all")47 orientation = request.args.get("orientation", "all")48 page = int(request.args.get("page", 1))49 per_page = int(request.args.get("per_page", 20))5051 if not query:52 return jsonify({"error": "query parameter is required"}), 4005354 data = search_images(query, image_type, orientation, per_page, page)55 results = []56 for hit in data.get("hits", []):57 results.append({58 "id": hit["id"],59 "tags": hit["tags"],60 "preview_url": hit["previewURL"],61 "web_url": hit["webformatURL"],62 "large_url": hit.get("largeImageURL", ""),63 "user": hit["user"],64 "views": hit["views"],65 "downloads": hit["downloads"],66 "page_url": hit["pageURL"]67 })68 return jsonify({"total": data.get("totalHits", 0), "results": results})697071@app.route("/video")72def video():73 query = request.args.get("query", "")74 if not query:75 return jsonify({"error": "query parameter is required"}), 40076 data = search_videos(query)77 results = []78 for hit in data.get("hits", []):79 videos = hit.get("videos", {})80 results.append({81 "id": hit["id"],82 "tags": hit["tags"],83 "user": hit["user"],84 "duration": hit["duration"],85 "medium_url": videos.get("medium", {}).get("url", ""),86 "small_url": videos.get("small", {}).get("url", ""),87 "page_url": hit["pageURL"]88 })89 return jsonify({"total": data.get("totalHits", 0), "results": results})909192if __name__ == "__main__":93 app.run(host="0.0.0.0", port=3000, debug=True)Pro tip: The 'webformatURL' field is a web-optimized JPEG capped at 1920px β use this for display. The 'largeImageURL' provides higher resolution but is not available on all images. The 'previewURL' is a small 150px thumbnail, good for gallery grids.
Expected result: GET /search?query=mountain returns a JSON array of image results with preview and full-size URLs.
Build a Node.js Pixabay Server
Build a Node.js Pixabay Server
For Node.js projects, install axios with 'npm install axios express'. The pattern is the same as Python: read the API key from the environment, build request parameters server-side, and forward only the cleaned image URLs to the frontend. This prevents the API key from being exposed in browser network requests. The server below handles both image and video searches, implements basic pagination, and returns a consistent response shape. Note that Pixabay returns a 'totalHits' field capped at 500 regardless of the actual total results β this is a Pixabay API limitation, not an error.
1const express = require('express');2const axios = require('axios');34const app = express();5app.use(express.json());67const PIXABAY_API_KEY = process.env.PIXABAY_API_KEY;8const IMAGE_URL = 'https://pixabay.com/api/';9const VIDEO_URL = 'https://pixabay.com/api/videos/';1011// Search images12app.get('/search', async (req, res) => {13 const {14 query,15 type = 'all',16 orientation = 'all',17 page = 1,18 per_page = 2019 } = req.query;2021 if (!query) {22 return res.status(400).json({ error: 'query parameter is required' });23 }2425 try {26 const { data } = await axios.get(IMAGE_URL, {27 params: {28 key: PIXABAY_API_KEY,29 q: query,30 image_type: type,31 orientation,32 per_page: Math.min(Math.max(Number(per_page), 3), 200),33 page: Number(page),34 safesearch: 'true'35 }36 });3738 const results = data.hits.map(hit => ({39 id: hit.id,40 tags: hit.tags,41 previewUrl: hit.previewURL,42 webUrl: hit.webformatURL,43 largeUrl: hit.largeImageURL || '',44 user: hit.user,45 views: hit.views,46 downloads: hit.downloads,47 pageUrl: hit.pageURL48 }));4950 res.json({ total: data.totalHits, results });51 } catch (err) {52 const status = err.response?.status || 500;53 res.status(status).json({ error: err.response?.data || err.message });54 }55});5657// Search videos58app.get('/video', async (req, res) => {59 const { query, page = 1, per_page = 20 } = req.query;6061 if (!query) {62 return res.status(400).json({ error: 'query parameter is required' });63 }6465 try {66 const { data } = await axios.get(VIDEO_URL, {67 params: {68 key: PIXABAY_API_KEY,69 q: query,70 per_page: Math.min(Math.max(Number(per_page), 3), 200),71 page: Number(page)72 }73 });7475 const results = data.hits.map(hit => ({76 id: hit.id,77 tags: hit.tags,78 user: hit.user,79 duration: hit.duration,80 mediumUrl: hit.videos?.medium?.url || '',81 smallUrl: hit.videos?.small?.url || '',82 pageUrl: hit.pageURL83 }));8485 res.json({ total: data.totalHits, results });86 } catch (err) {87 const status = err.response?.status || 500;88 res.status(status).json({ error: err.response?.data || err.message });89 }90});9192app.listen(3000, '0.0.0.0', () => {93 console.log('Pixabay integration server running on port 3000');94});Pro tip: Pixabay's totalHits is capped at 500. If your UI needs to display 'Found 10,000 results', use the 'total' field from the raw API response which is uncapped, but pagination is only supported up to 500 results.
Expected result: GET /search?query=sunset returns a JSON object with total count and an array of image results with URLs and metadata.
Deploy and Configure the .replit File
Deploy and Configure the .replit File
Click the Deploy button in Replit and choose Autoscale deployment. Pixabay image search is a request-driven workload with no persistent background tasks, making Autoscale the most cost-effective choice. Autoscale scales to zero when no requests are coming in and scales up instantly when queries arrive. In your .replit configuration file, set the run command to match your language. For Node.js, that is 'node server.js'. For Python, it is 'python app.py'. Also configure the port mapping so Replit routes external traffic to your server: After deploying, your Pixabay search endpoint will be available at your Replit deployment URL. Test it by visiting /search?query=forest in your browser β you should receive JSON results with image URLs. The API key remains hidden in Replit Secrets and is never exposed to clients.
1[[ports]]2internalPort = 30003externalPort = 8045[deployment]6run = ["node", "server.js"]7deploymentTarget = "cloudrun"Pro tip: Use Autoscale (not Reserved VM) for Pixabay search β the workload is purely reactive. Reserved VM is better for always-on services like bots or queues.
Expected result: Your deployed Replit app serves Pixabay search results at the public deployment URL with the API key securely stored in Secrets.
Common use cases
Contextual Blog Image Suggestions
A Replit-backed blogging tool searches Pixabay for images matching a blog post's title or topic, presents a grid of free options to the writer, and inserts the chosen image URL into the post's featured image field. Because Pixabay images are free to use without attribution requirements in most cases, writers can publish immediately without licensing checks.
Build a Flask endpoint that accepts a blog topic keyword, queries the Pixabay API for the top 12 horizontal images, and returns a JSON array of preview URLs and Pixabay page links.
Copy this prompt to try it in Replit
Automated Social Media Image Finder
A Replit automation script receives a list of scheduled social media posts, extracts keywords from each post's text, fetches matching Pixabay images for each, and saves the best-matching image URL alongside each post for the scheduler to attach. This saves time in the content pipeline by auto-suggesting visuals for every post.
Create a Node.js script that reads a CSV of social media captions, queries Pixabay for one matching image per caption, and outputs a new CSV with an added column for the image URL.
Copy this prompt to try it in Replit
Category-Based Illustration Gallery
A Replit web app displays a browsable illustration gallery powered by Pixabay, letting users filter by category (nature, technology, business, etc.) and media type (photo, illustration, or vector). The backend handles Pixabay pagination so the frontend can implement infinite scroll without exposing the API key.
Write an Express server with a /gallery route that accepts category, image_type, and page parameters, fetches results from Pixabay, and returns paginated image data.
Copy this prompt to try it in Replit
Troubleshooting
HTTP 400 Bad Request when querying the Pixabay API
Cause: The API key is missing from the request, malformed, or the API key value in Replit Secrets contains extra whitespace or quotes.
Solution: Open the Replit Secrets pane and verify the PIXABAY_API_KEY value contains only the raw key string with no surrounding quotes or spaces. Print the key value in your code during debugging (print(repr(os.environ['PIXABAY_API_KEY']))) to check for invisible characters.
1# Debug: check for extra whitespace2import os3key = os.environ.get('PIXABAY_API_KEY', '')4print(f'Key length: {len(key)}, repr: {repr(key[:10])}')Empty results array even though the search term should match images
Cause: Pixabay search uses exact phrase matching by default. Multi-word queries with special characters may return zero results, or the safesearch filter may be excluding some content.
Solution: Simplify the query to one or two keywords and URL-encode special characters properly. The requests library and axios handle URL encoding automatically, but manually constructed URLs may not. Also try removing the safesearch parameter to see if that is filtering out results.
1# Try a broad single-keyword query first2params = {3 'key': PIXABAY_API_KEY,4 'q': 'nature', # start simple5 'per_page': 56}Rate limit errors (HTTP 429) after many searches
Cause: The Pixabay free API allows 100 requests per 60 seconds per key. High-frequency searching, pagination loops, or automated scripts can hit this limit quickly.
Solution: Implement caching for repeated queries using a dictionary keyed on the search term. Cache results for 5-10 minutes to avoid redundant requests. For batch processing scripts, add a short sleep between requests to stay within rate limits.
1import time2cache = {}34def cached_search(query, **kwargs):5 if query in cache and time.time() - cache[query]['ts'] < 300:6 return cache[query]['data']7 data = search_images(query, **kwargs)8 cache[query] = {'data': data, 'ts': time.time()}9 return dataPIXABAY_API_KEY environment variable returns None or KeyError
Cause: The secret was added after the Repl was started without a restart, or the secret name in the code does not exactly match the key name in Replit Secrets (case-sensitive).
Solution: Restart the Repl after adding a new secret β Replit injects secrets at startup. Also verify the exact key name in the Secrets pane matches what your code references. PIXABAY_API_KEY and pixabay_api_key are different names.
Best practices
- Store PIXABAY_API_KEY in Replit Secrets (lock icon π) and never include it in client-side JavaScript or HTML.
- Always proxy Pixabay API calls through your Replit backend β never call the Pixabay API directly from the browser, as that would expose your key.
- Cache search results for popular or repeated queries to stay within the 100 requests/60 seconds rate limit.
- Use the 'webformatURL' for display (web-optimized JPEG up to 1920px) and 'previewURL' for thumbnails in grids β avoid requesting 'largeImageURL' unless high resolution is truly needed.
- Include the Pixabay page link (pageURL field) alongside displayed images so users can visit the original page for full attribution and licensing details.
- Use the 'safesearch=true' parameter in production apps to prevent adult content from appearing in search results.
- Deploy on Autoscale rather than Reserved VM for image search APIs β Autoscale is more cost-efficient for request-driven workloads with variable traffic.
- Respect Pixabay's API terms: do not use the API to download and redistribute images in bulk or as a replacement for the Pixabay website.
Alternatives
Shutterstock offers a vastly larger catalog of premium licensed stock photos and videos, making it a better choice when image quality and editorial licensing matter more than cost.
Getty Images provides premium editorial and creative photography with verified licensing, making it the right choice for professional media applications where image credibility is critical.
Vimeo is the better option when you need high-quality video hosting with player customization and privacy controls rather than free stock image searches.
Frequently asked questions
How do I store my Pixabay API key in Replit?
Click the lock icon π in the Replit sidebar to open the Secrets pane. Add a secret with the key PIXABAY_API_KEY and your Pixabay key as the value. Access it in Python with os.environ['PIXABAY_API_KEY'] and in Node.js with process.env.PIXABAY_API_KEY. Restart the Repl after adding the secret.
Is the Pixabay API free to use?
Yes. Pixabay provides free API access to all registered users with a limit of 100 requests per 60 seconds. There is no paid tier for the API itself, though creating a free Pixabay account is required to obtain an API key. Pixabay images are free to use under the Pixabay Content License for most commercial and personal use cases.
Can I search for videos with the Pixabay API?
Yes. Pixabay has a separate video endpoint at https://pixabay.com/api/videos/ that works with the same API key. Search parameters are similar to the image endpoint. The response includes video URLs in multiple resolutions (large, medium, small, tiny) along with duration, user, and tag metadata.
Does Pixabay require attribution for images used in my app?
The Pixabay Content License does not legally require attribution, but Pixabay strongly recommends crediting the photographer and linking to the Pixabay page as a courtesy. For commercial applications, always review the full Pixabay Content License at pixabay.com/service/license-summary/ to confirm your specific use case is covered.
Why does my Pixabay API search return a maximum of 500 results?
The Pixabay API limits pagination to 500 results maximum (totalHits is capped). This is a deliberate API limitation rather than a bug. The 'total' field in the raw response shows the real result count, but you can only retrieve results across the first 500. For broader searches, use more specific keywords or multiple smaller queries.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation