What You'll Build
A dynamic product catalog powered by Google Sheets. Update your inventory in real-time without touching code. Perfect for small e-commerce stores, marketplaces, and MVPs.
Step 1: Set Up Your Product Sheet
Create a Google Sheet with these columns:
- Product Name - Name of the product
- Price - Product price (numeric)
- Description - Short product description
- Image URL - Link to product image
- Category - Product category (Electronics, Clothing, etc.)
- In Stock - Yes or No
Add a few sample products to test with.
Step 2: Connect Your Sheet
Add your Google Sheet to SheetAPI.pro and get your API credentials. The API will automatically sync with your Sheet in real-time.
Step 3: Build the Frontend
Use the HTML/JavaScript snippet to create a product grid with category filtering. The code fetches products from the API and renders them dynamically.
Step 4: Add Filtering & Search
The provided code includes category filtering. You can extend it with search functionality, price range filters, or sorting options.
Step 5: Style & Deploy
Customize the CSS to match your brand. Deploy to any static hosting platform. Your product data updates automatically when you edit the Sheet!
Use Cases
- Small e-commerce - Simple product catalog without complex databases
- Market vendors - Update inventory on-the-go from mobile
- MVPs - Validate your product idea before building full infrastructure
- Dropshipping - Sync products from supplier sheets
Advanced Features
- Add a shopping cart using localStorage
- Integrate with Stripe or PayPal for payments
- Set up webhooks to sync with other platforms
- Add product variants (size, color) with additional columns
- Track inventory levels and low-stock alerts
<!DOCTYPE html>
<html>
<head>
<title>Product Catalog</title>
<style>
body { font-family: Arial, sans-serif; max-width: 1200px; margin: 0 auto; padding: 20px; background: #f5f5f5; }
h1 { text-align: center; }
.filters { display: flex; gap: 10px; margin-bottom: 20px; justify-content: center; }
.filters button { padding: 10px 20px; border: none; background: #ddd; cursor: pointer; border-radius: 4px; }
.filters button.active { background: #667eea; color: white; }
.products { display: grid; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); gap: 20px; }
.product { background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
.product img { width: 100%; height: 200px; object-fit: cover; border-radius: 4px; }
.product h3 { margin: 10px 0; }
.product .price { font-size: 1.5rem; color: #667eea; font-weight: bold; }
.product .stock { color: green; font-size: 0.9rem; }
.product .out-of-stock { color: red; }
</style>
</head>
<body>
<h1>Our Products</h1>
<div class="filters" id="filters"></div>
<div class="products" id="products"></div>
<script src="catalog.js"></script>
</body>
</html>
const API_URL = 'https://sheetapi.pro/api/v1/sheets/YOUR_SHEET_ID/data';
const API_KEY = 'YOUR_API_KEY';
let allProducts = [];
let currentCategory = 'all';
async function loadProducts() {
try {
const response = await fetch(API_URL, {
headers: { 'Authorization': `Bearer ${API_KEY}` }
});
const data = await response.json();
allProducts = data.data || [];
renderFilters();
renderProducts();
} catch (error) {
console.error('Failed to load products:', error);
}
}
function renderFilters() {
const categories = ['all', ...new Set(allProducts.map(p => p.Category))];
const filtersDiv = document.getElementById('filters');
filtersDiv.innerHTML = categories.map(cat =>
`<button class="${cat === currentCategory ? 'active' : ''}"
onclick="filterByCategory('${cat}')">${cat}</button>`
).join('');
}
function filterByCategory(category) {
currentCategory = category;
renderFilters();
renderProducts();
}
function renderProducts() {
const filtered = currentCategory === 'all'
? allProducts
: allProducts.filter(p => p.Category === currentCategory);
const productsDiv = document.getElementById('products');
productsDiv.innerHTML = filtered.map(product => `
<div class="product">
<img src="${product['Image URL']}" alt="${product['Product Name']}">
<h3>${product['Product Name']}</h3>
<p>${product.Description}</p>
<div class="price">$${product.Price}</div>
<div class="${product['In Stock'] === 'Yes' ? 'stock' : 'out-of-stock'}">
${product['In Stock'] === 'Yes' ? '✓ In Stock' : '✗ Out of Stock'}
</div>
</div>
`).join('');
}
loadProducts();
import React, { useState, useEffect } from 'react';
const API_URL = 'https://sheetapi.pro/api/v1/sheets/YOUR_SHEET_ID/data';
const API_KEY = 'YOUR_API_KEY';
function ProductCatalog() {
const [products, setProducts] = useState([]);
const [category, setCategory] = useState('all');
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch(API_URL, {
headers: { 'Authorization': `Bearer ${API_KEY}` }
})
.then(res => res.json())
.then(data => {
setProducts(data.data || []);
setLoading(false);
})
.catch(err => console.error(err));
}, []);
const categories = ['all', ...new Set(products.map(p => p.Category))];
const filtered = category === 'all'
? products
: products.filter(p => p.Category === category);
if (loading) return <div>Loading...</div>;
return (
<div>
<h1>Product Catalog</h1>
<div className="filters">
{categories.map(cat => (
<button
key={cat}
className={cat === category ? 'active' : ''}
onClick={() => setCategory(cat)}
>
{cat}
</button>
))}
</div>
<div className="products">
{filtered.map((product, i) => (
<div key={i} className="product">
<img src={product['Image URL']} alt={product['Product Name']} />
<h3>{product['Product Name']}</h3>
<p>{product.Description}</p>
<div className="price">${product.Price}</div>
<div className={product['In Stock'] === 'Yes' ? 'stock' : 'out-of-stock'}>
{product['In Stock'] === 'Yes' ? '✓ In Stock' : '✗ Out of Stock'}
</div>
</div>
))}
</div>
</div>
);
}
export default ProductCatalog;
import requests
from flask import Flask, jsonify, request
app = Flask(__name__)
SHEET_ID = 'YOUR_SHEET_ID'
API_KEY = 'YOUR_API_KEY'
API_URL = f'https://sheetapi.pro/api/v1/sheets/{SHEET_ID}/data'
@app.route('/api/products', methods=['GET'])
def get_products():
headers = {'Authorization': f'Bearer {API_KEY}'}
# Get optional category filter
category = request.args.get('category')
response = requests.get(API_URL, headers=headers)
if response.status_code == 200:
products = response.json().get('data', [])
# Filter by category if specified
if category and category != 'all':
products = [p for p in products if p.get('Category') == category]
return jsonify({
'success': True,
'count': len(products),
'products': products
})
else:
return jsonify({'success': False, 'error': 'Failed to fetch products'}), 500
if __name__ == '__main__':
app.run(debug=True)
<?php
// products-api.php
header('Content-Type: application/json');
$sheetId = 'YOUR_SHEET_ID';
$apiKey = 'YOUR_API_KEY';
$apiUrl = "https://sheetapi.pro/api/v1/sheets/{$sheetId}/data";
$category = $_GET['category'] ?? 'all';
$ch = curl_init($apiUrl);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"Authorization: Bearer {$apiKey}"
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode === 200) {
$data = json_decode($response, true);
$products = $data['data'] ?? [];
// Filter by category
if ($category !== 'all') {
$products = array_filter($products, function($p) use ($category) {
return $p['Category'] === $category;
});
$products = array_values($products); // Re-index
}
echo json_encode([
'success' => true,
'count' => count($products),
'products' => $products
]);
} else {
http_response_code(500);
echo json_encode(['success' => false, 'error' => 'Failed to fetch']);
}
?>
# Get all products
curl -X GET https://sheetapi.pro/api/v1/sheets/YOUR_SHEET_ID/data \
-H "Authorization: Bearer YOUR_API_KEY"
# Add a new product
curl -X POST https://sheetapi.pro/api/v1/sheets/YOUR_SHEET_ID/data \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"Product Name": "Wireless Headphones",
"Price": "89.99",
"Description": "Premium noise-canceling headphones",
"Image URL": "https://example.com/headphones.jpg",
"Category": "Electronics",
"In Stock": "Yes"
}'