Writing RPG content is one of the biggest time sinks in indie game development. One NPC needs an intro, a quest hook, a refusal line, and a reward acknowledgement. Multiply that by 50 NPCs and you've spent two weeks on dialogue trees players will skip in 30 seconds.
The GameForge Content API solves this with a simple trade: you send a structured description of what you want - a weary innkeeper, a cursed blade, a fetch quest in dark ruins - and get back structured JSON you can drop straight into your game. No prompt engineering, no parsing free-form text, no local model to run.
What It Generates
Four content types, all returning clean JSON:
| Endpoint | What you get |
|---|---|
/lore/worldbuild |
Location descriptions, faction histories, creation myths |
/quest/generate |
Full quest: name, hook, objectives list, reward text |
/item/description |
Lore-rich item descriptions with trait integration |
/npc/dialogue |
Multi-line dialogue with per-line emotion tags |
Supported genres: high_fantasy dark_fantasy sci_fi western horror cozy cyberpunk
Quick Start
pip install requests
- Sign up at RapidAPI
- Search "GameForge Content" by Circle of Wizards
- Subscribe free (BASIC plan)
- Copy your
X-RapidAPI-Key
import requests
KEY = "YOUR_RAPIDAPI_KEY"
HOST = "gameforge-content.p.rapidapi.com"
BASE = f"https://{HOST}"
HEADERS = {
"X-RapidAPI-Key": KEY,
"X-RapidAPI-Host": HOST,
"Content-Type": "application/json",
}
Generate World Lore
def generate_lore(name: str, subject_type: str, traits: list[str],
genre: str = "dark_fantasy", words: int = 80,
style: str = "in_world_document") -> str:
# style options: in_world_document | encyclopaedia_entry | oral_tradition | official_record
r = requests.post(f"{BASE}/lore/worldbuild", headers=HEADERS, json={
"subject": {"type": subject_type, "name": name, "traits": traits},
"context": {"genre": genre, "word_count": words, "style": style, "narrator": "omniscient"},
})
r.raise_for_status()
return r.json()["lore"]
print(generate_lore(
name="The Shattered Spire",
subject_type="location",
traits=["ancient", "cursed", "labyrinthine"],
))
Output:
The Spire's walls, once pristine marble, weep a black ichor that stains the
hands of those who wander its endless halls. Each corridor twists upon itself,
a labyrinth born not of design but of malice. Deep within, the stone remembers
the cataclysm that shattered its peak, whispering secrets to those who listen
too closely - secrets that linger, long after the listener has gone.
Change style to "oral_tradition" for tavern rumor text, or "encyclopaedia_entry" for codex entries.
Generate Quests
def generate_quest(quest_type: str, difficulty: str, setting: str,
reward_type: str, genre: str = "dark_fantasy",
party_size: int = 4) -> dict:
r = requests.post(f"{BASE}/quest/generate", headers=HEADERS, json={
"quest": {
"type": quest_type, # fetch | rescue | investigate | escort | eliminate
"difficulty": difficulty, # easy | medium | hard
"setting": setting,
"reward_type": reward_type, # gold | knowledge | item | reputation
},
"context": {"genre": genre, "party_size": party_size},
})
r.raise_for_status()
return r.json()
quest = generate_quest(
quest_type="fetch",
difficulty="medium",
setting="cursed_ruins",
reward_type="knowledge",
)
print(f"Quest: {quest['name']}")
print(f"\n{quest['description']}\n")
print("Objectives:")
for obj in quest["objectives"]:
print(f" ? {obj}")
print(f"\nReward: {quest['rewards_text']}")
Output:
Quest: Whispers in the Rubble
A merchant's daughter vanished near the cursed ruins of Korrath, where the ground
itself seems to hunger. Locals speak of a rasping voice that lures the curious into
crumbling halls. Find the girl - or what remains of her - and silence the source
of the whispers.
Objectives:
? Search the outer ruins for traces of the missing girl
? Descend into the catacomb beneath the collapsed tower
? Recover the merchant's signet ring from the whispering shade
? Escape the ruins before the curse claims another soul
Reward: The merchant presses a pouch of silver into your palm, along with a
tarnished locket that hums faintly - a charm against lies, he claims. And in
your dreams that night, the whispers are a little quieter.
Generate Item Descriptions
def generate_item(name: str, item_type: str, rarity: str,
traits: list[str], genre: str = "dark_fantasy") -> str:
r = requests.post(f"{BASE}/item/description", headers=HEADERS, json={
"item": {"name": name, "type": item_type, "rarity": rarity, "traits": traits},
"context": {"genre": genre, "detail_level": "rich"},
})
r.raise_for_status()
return r.json()["description"]
print(generate_item(
name="Veilblade",
item_type="weapon",
rarity="legendary",
traits=["shadow-forged", "cursed", "sentient"],
))
Output:
Forged from a star's dying light, this blade cuts not flesh but fate itself.
Those who wield it see the path to victory - and the cost.
Generate NPC Dialogue
def generate_dialogue(name: str, role: str, personality: str,
player_action: str, setting: str,
genre: str = "dark_fantasy", tone: str = "neutral") -> list[dict]:
r = requests.post(f"{BASE}/npc/dialogue", headers=HEADERS, json={
"character": {"name": name, "role": role, "personality": personality},
"player_action": player_action,
"context": {"genre": genre, "tone": tone, "setting": setting},
})
r.raise_for_status()
return r.json()["lines"]
lines = generate_dialogue(
name="Mira",
role="innkeeper",
personality="weary, suspicious, secretly kind",
player_action="ask about recent travelers",
setting="roadside_inn",
tone="tense",
)
for line in lines:
print(f"[{line['emotion']:12}] {line['text']}")
Output:
[suspicious ] You look like trouble. Most do, these days. But your coin spends the same.
[fearful ] Strange times when even the shadows seem to watch. Lock your door tonight.
[impatient ] The last traveler who asked too many questions left in a hurry. Or didn't leave at all.
The emotion tag drives your dialogue system - route to the right voice line, portrait expression, or animation state without any extra parsing.
Bulk Content Pipeline
Generate an entire zone's worth of content in one script and cache it as JSON:
import json
from pathlib import Path
from time import sleep
def generate_zone(zone_name: str, genre: str, npcs: list[dict],
items: list[dict], output_path: str):
zone = {"name": zone_name, "genre": genre, "lore": "", "npcs": [], "items": []}
# Zone lore
zone["lore"] = generate_lore(zone_name, "location",
traits=["mysterious", "dangerous"],
genre=genre, words=100)
# NPCs
for npc in npcs:
print(f" Generating {npc['name']}...")
lines = generate_dialogue(
name=npc["name"], role=npc["role"],
personality=npc.get("personality", "neutral"),
player_action="player greets",
setting=zone_name, genre=genre,
)
zone["npcs"].append({**npc, "dialogue": lines})
sleep(0.5) # be kind to rate limits on free tier
# Items
for item in items:
print(f" Generating {item['name']}...")
desc = generate_item(item["name"], item["type"],
item["rarity"], item.get("traits", []), genre)
zone["items"].append({**item, "description": desc})
sleep(0.5)
Path(output_path).write_text(json.dumps(zone, indent=2))
print(f"\nSaved to {output_path}")
generate_zone(
zone_name="The Ashwood",
genre="dark_fantasy",
npcs=[
{"name": "Brother Aldus", "role": "priest", "personality": "devout, haunted"},
{"name": "Old Brennan", "role": "innkeeper", "personality": "gossipy, cowardly"},
],
items=[
{"name": "Soulbrand", "type": "weapon", "rarity": "rare", "traits": ["fire-touched"]},
{"name": "Grey Cloak", "type": "armor", "rarity": "uncommon", "traits": ["woven from fog"]},
],
output_path="ashwood_zone.json",
)
The output file drops straight into a Godot ResourceLoader or Unity AssetDatabase import step.
Game Engine Integration
Godot (GDScript)
# Load pre-generated content at runtime
func load_npc_dialogue(npc_name: String) -> Array:
var file = FileAccess.open("res://content/zone_data.json", FileAccess.READ)
var data = JSON.parse_string(file.get_as_text())
for npc in data["npcs"]:
if npc["name"] == npc_name:
return npc["dialogue"] # [{text, emotion}, ...]
return []
# Drive your dialogue system
func play_dialogue(lines: Array):
for line in lines:
$Portrait.set_emotion(line["emotion"]) # map to sprite frame
$DialogueBox.text = line["text"]
await $DialogueBox.finished
Unity (C#)
[System.Serializable]
public class DialogueLine { public string text; public string emotion; }
[System.Serializable]
public class NpcData { public string name; public DialogueLine[] dialogue; }
// Load at scene start, play via your dialogue runner
NpcData npc = JsonUtility.FromJson<NpcData>(Resources.Load<TextAsset>("npcs/mira").text);
Why an API Over a Local Model?
- No GPU required - works on any machine, including CI/CD content pipelines
- Structured output every time - no prompt engineering, no JSON parsing failures
- Genre consistency - vocabulary and tone stay coherent across all calls in a session
- Free tier covers a full indie game's worth of content (100 req/month on BASIC)
PRO ($9.99/mo) unlocks higher call limits - useful if you're running the pipeline on every content update or letting players trigger generation at runtime.
The API is live on RapidAPI - search "GameForge Content" by Circle of Wizards. If you ship something with it, drop the link in the comments - always curious what people build.
Top comments (0)