I built NewsFavored — a free AI-powered hashtag, caption, and YouTube SEO toolkit built specifically for Indian creators. Stack: static HTML/CSS/JS on shared hosting + a PHP proxy to Claude API. Zero frameworks, zero databases, zero monthly infra cost beyond the API. Here's the full architecture.
The Problem I Was Solving
Most AI creator tools are built for Western markets. Generic hashtag generators return the same tags regardless of whether your content is about Diwali or a gym transformation in Mumbai. Caption tools produce robotic English that doesn't resonate with Indian audiences who naturally mix Hindi and English.
I'm a UX Manager by day and an indie builder on weekends. I wanted to build something that actually understood the Indian creator context — Bollywood references, cricket seasons, Hinglish tone, city-specific hashtags, and the very different way Indian audiences consume short-form content.
The result: NewsFavored — 8 AI-powered tools, all free, all designed for Indian Instagram and YouTube creators.
Tech Stack Decision: Why No Framework
I made a deliberate choice to use zero JavaScript frameworks and zero backend infrastructure beyond PHP shared hosting.
Reasons:
Deployment simplicity — I upload files via Hostinger's File Manager. No CI/CD, no Docker, no build steps. A complete deploy takes 3 minutes.
Cost — Shared hosting is ₹200/month. The only variable cost is Claude API usage. For a bootstrapped tool monetised via AdSense, keeping fixed costs near zero matters.
Maintenance — No dependency hell. No
npm auditnightmares. The site I build today will work in 5 years without touching it.Performance — Static HTML loads fast, especially on Indian mobile networks where 4G speeds vary significantly.
The tradeoff: no reactivity, no component reuse, more verbose HTML. For a tool site with 8 pages doing the same pattern, this is acceptable.
The Architecture
Browser (Static HTML/CSS/JS)
│
│ POST /ai-proxy.php
│ { tool, input, platform, lang }
▼
ai-proxy.php ◄── API key lives here ONLY
│
│ POST https://api.anthropic.com/v1/messages
│ Authorization: x-api-key: sk-ant-...
▼
Claude API (Haiku 4.5 or Sonnet 4.6)
│
│ JSON response
▼
ai-proxy.php (validates, strips markdown fences)
│
│ { success: true, data: {...} }
▼
Browser (renders result)
The key design principle: the API key never touches the browser. All API calls go through the PHP proxy on the server. Someone can inspect every network request and they will never see the key.
The PHP Proxy — Security Layers
This is the most important file in the project. I built 6 security layers into it:
<?php
define('ANTHROPIC_API_KEY', 'sk-ant-YOUR_KEY_HERE');
define('ALLOWED_ORIGIN', 'https://newsfavored.com');
define('MAX_INPUT_CHARS', 600);
define('RATE_LIMIT', 20); // requests per IP per hour
define('RATE_WINDOW', 3600);
// Layer 1: Method guard — only POST
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
http_response_code(405);
exit;
}
// Layer 2: Origin lock — block external callers
$origin = $_SERVER['HTTP_ORIGIN'] ?? '';
$referer = $_SERVER['HTTP_REFERER'] ?? '';
$origin_ok = $origin && strpos($origin, ALLOWED_ORIGIN) === 0;
$referer_ok = $referer && strpos($referer, ALLOWED_ORIGIN) === 0;
if (!$origin_ok && !$referer_ok) {
http_response_code(403);
echo json_encode(['error' => 'Forbidden']);
exit;
}
// Layer 3: Rate limiter by hashed IP
function check_rate_limit(): bool {
$ip_hash = md5($_SERVER['REMOTE_ADDR'] ?? 'unknown');
$dir = sys_get_temp_dir() . '/nf_ratelimit/';
if (!is_dir($dir)) mkdir($dir, 0700, true);
$file = $dir . $ip_hash . '.json';
$now = time();
$data = ['count' => 0, 'window_start' => $now];
if (file_exists($file)) {
$raw = @file_get_contents($file);
if ($raw) $data = json_decode($raw, true) ?: $data;
}
if ($now - $data['window_start'] > RATE_WINDOW) {
$data = ['count' => 0, 'window_start' => $now];
}
$data['count']++;
@file_put_contents($file, json_encode($data), LOCK_EX);
return $data['count'] <= RATE_LIMIT;
}
// Layer 4: Tool whitelist — reject unknown tools
$allowed_tools = [
'hashtag_instagram', 'hashtag_youtube', 'caption',
'youtube_title', 'bio', 'reel_script', 'content_ideas'
];
if (!in_array($tool, $allowed_tools, true)) {
http_response_code(400);
echo json_encode(['error' => 'Unknown tool']);
exit;
}
// Layer 5: Input sanitisation
$input = mb_substr(strip_tags($input), 0, MAX_INPUT_CHARS);
// Layer 6: Structured JSON output validation
$parsed = json_decode($text, true);
if (!$parsed) {
http_response_code(502);
echo json_encode(['error' => 'Invalid AI response']);
exit;
}
The rate limiter stores hashed IPs (never raw IPs) as JSON files in /tmp/. No database required. It resets every hour. 20 requests per hour per IP is generous for normal use but blocks abuse.
The Prompt Architecture
The most interesting engineering challenge: getting Claude to always return valid JSON, never markdown, never preamble.
Here's the system prompt pattern I use for the hashtag generator:
$system = <<<PROMPT
You are an expert Instagram SEO strategist specialising in Indian creator content.
Write output in {$lang}.
Given the user's content topic, return EXACTLY 30 Instagram hashtags as JSON:
{
"mega": ["#tag1","#tag2","#tag3","#tag4","#tag5"],
"mid": ["#tag1",...,"#tag10"],
"niche": ["#tag1",...,"#tag15"]
}
Rules:
- mega: 5 hashtags with 1M+ posts
- mid: 10 hashtags with 100K–1M posts
- niche: 15 hashtags with under 100K posts, include 3-4 India-specific
- Include # on every tag. No spaces. No duplicates.
- Respond with valid JSON ONLY. No preamble, no markdown, no explanation.
PROMPT;
The key phrase is the last line: "Respond with valid JSON ONLY." Without this, Claude adds helpful preamble like "Here are your hashtags:" which breaks json_decode().
I also strip markdown fences defensively in PHP before parsing:
$text = preg_replace('/^```
(?:json)?\s*/m', '', $text);
$text = preg_replace('/\s*
```$/m', '', $text);
$text = trim($text);
This handles cases where the model adds code fences despite being told not to.
Model Routing — Haiku vs Sonnet
I route different tools to different models based on task complexity:
$creative_tools = ['caption', 'reel_script'];
$model = in_array($tool, $creative_tools, true)
? 'claude-sonnet-4-6' // $3/$15 per MTok — better creative quality
: 'claude-haiku-4-5-20251001'; // $1/$5 per MTok — fast, cheap, structured
Haiku handles hashtag generation, YouTube tags, bio generation, and content ideas — all structured JSON tasks where the schema is tight and the outputs are formulaic. It's fast (under 1 second) and cheap.
Sonnet handles caption writing and reel scripts — tasks where tone, cultural nuance, and creative quality are visible to the end user. The quality difference is noticeable in A/B testing.
At 500 requests/day with this routing split, the API cost is approximately $2.50/day — well within AdSense revenue for a creator tools niche site.
The Frontend AI Engine
All 8 tool pages share a single ai-engine.js file with convenience wrappers:
const AI = (() => {
const PROXY = '/ai-proxy.php';
async function call(tool, input, opts = {}) {
const res = await fetch(PROXY, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
tool,
input,
platform: opts.platform || 'instagram',
lang: opts.lang || 'english',
}),
});
const json = await res.json();
if (!res.ok || !json.success) throw new Error(json.error);
return json.data;
}
return {
instagramHashtags: (input, lang) =>
call('hashtag_instagram', input, { platform: 'instagram', lang }),
youtubeHashtags: (input, lang) =>
call('hashtag_youtube', input, { platform: 'youtube', lang }),
caption: (input, lang) => call('caption', input, { lang }),
youtubeTitle: (input, lang) => call('youtube_title', input, { lang }),
bio: (input, lang) => call('bio', input, { lang }),
reelScript: (input, lang) => call('reel_script', input, { lang }),
contentIdeas: (input, p, lang) => call('content_ideas', input, { platform: p, lang }),
};
})();
Each tool page then calls one line:
const data = await AI.instagramHashtags(input, lang);
NF.renderHashtagClusters(outputDiv, data);
The tool pages contain zero API logic. They just call AI methods and pass data to render helpers.
The India-Specific Problem
The real product differentiation isn't technical — it's in the prompts.
Every system prompt includes this instruction:
Always factor in Indian creator context: Bollywood, cricket, Indian
festivals, desi culture, Indian cities, and popular Indian niches.
Hashtags and content must reflect what Indian audiences actually search.
And for language routing:
Hindi: Write output entirely in Hindi (Devanagari script).
Hinglish: Write in Hinglish — a natural mix of English and Hindi words
written in Roman script, the way Indian creators actually speak.
English: Write in English.
The result: typing "gym workout" with Hinglish selected returns #gymkaro, #fitnesswala, #desifit — tags that Indian creators and their audiences actually use, not translated equivalents from Western tools.
SEO Architecture
Since the site is static HTML, all SEO is baked into the build:
- Each tool page has
FAQPage+BreadcrumbList+SoftwareApplicationJSON-LD - Blog posts have full
Articleschema withdatePublished,author,mainEntityOfPage - All pages have OG + Twitter card tags for WhatsApp and social sharing previews
- Utility pages (
/about/,/privacy/,/contact/,/terms/) are noindexed to preserve crawl budget - Blog posts are 1,400–1,700 words targeting India-specific long-tail keywords
The target keyword strategy: skip the impossible broad terms (instagram hashtag generator, caption generator) and own the India-specific long-tail where DA-90 global tools aren't optimising (instagram hashtag generator india, caption generator hinglish, reel script generator india).
What I'd Do Differently
1. Shared CSS from the start. I initially had inline styles everywhere. Refactoring to a single shared.css file took a full session. Start with design tokens from day one.
2. Prompt versioning. Prompts change as you iterate. I now keep a prompts/ directory with version comments. Without this, you lose track of what changed when quality shifts.
3. Build the blog first. New domains need content authority before tool pages rank. I should have launched with 5 blog posts before building any tools.
Live Project
NewsFavored is live at newsfavored.com. All 8 tools are free, no login required. The full stack is exactly what's described here — no magic, no complex infrastructure.
If you're building something similar for a regional market, the approach generalises well: static hosting + PHP proxy + Claude API covers most use cases without the operational overhead of a full backend.
Happy to answer questions about the architecture in the comments.
*Tags: #ai #buildinpublic #php #sideproject #indiandev
Top comments (0)