Skip to content

Godot Integration Tutorial

This tutorial walks you through integrating Quest Data into a simple Idle/Clicker game. By the end, your game will track events, progression, purchases, remote config, and leaderboards. Each step builds on the previous one.

Start by tracking the most important player actions. In your main game script:

extends Node
func _ready():
# Track game start with useful context
QuestData.track("game_start", {
"version": ProjectSettings.get_setting("application/config/version", "1.0"),
"platform": OS.get_name(),
"language": TranslationServer.get_locale()
})
func _on_click_button_pressed():
# Don't track every click — track milestones instead
total_clicks += 1
if total_clicks % 100 == 0:
QuestData.track("milestone_reached", {
"type": "clicks",
"count": total_clicks
})

Tell Quest Data who your players are so you can segment them later:

func _ready():
# Set properties once on game start
QuestData.set_user_properties({
"platform": OS.get_name(),
"language": TranslationServer.get_locale(),
"first_launch": not FileAccess.file_exists("user://save.dat")
})
func _on_level_up(new_level: int):
# Update as the player progresses
QuestData.set_user_property("highest_level", new_level)

These properties appear in the Player Explorer and can be used for Segments.

Track levels, stages, or any sequential content. The SDK automatically calculates how long each attempt takes:

func start_level(level_id: String, difficulty: String = "normal"):
QuestData.start_progression(level_id, difficulty)
func complete_level(level_id: String, score: int):
QuestData.complete_progression(level_id, score)
# Duration is calculated automatically!
func fail_level(level_id: String, reason: String):
QuestData.fail_progression(level_id, reason)

In your game:

# Player enters level 5 on hard mode
start_level("level_5", "hard")
# Player beats it with a score of 1200
complete_level("level_5", 1200)
# Or dies
fail_level("level_5", "out_of_health")

What you see in the dashboard: Completion rates per level, average duration, most common failure reasons. This tells you where your difficulty curve is wrong.

See Progression Tracking Reference for full API details.

Track in-app purchases to see revenue in the dashboard:

func _on_purchase_completed(product_id: String, price: float):
QuestData.track_purchase(product_id, price, "USD", {
"store": "google_play",
"player_level": current_level
})

Common products for idle games:

# Coin packs
QuestData.track_purchase("coins_1000", 0.99)
QuestData.track_purchase("coins_10000", 4.99)
# Speed boost
QuestData.track_purchase("speed_boost_1h", 1.99, "USD", {
"boost_type": "speed",
"duration_hours": 1
})
# Remove ads
QuestData.track_purchase("remove_ads", 2.99)

What you see in the dashboard: Revenue timeline, top-selling products, ARPPU, paying vs. free players.

See Purchase Tracking Reference and the Monetization Guide.

Change game settings without pushing an update. First, create some config keys in the dashboard under Configuration > Remote Config:

KeyValueDescription
coin_multiplier1.0Coins earned per click
daily_bonus100Free coins per day
show_adstrueWhether to show ads

Then read them in your game:

func _ready():
# Fetch latest config from server
QuestData.fetch_remote_config()
func get_coins_per_click() -> float:
# Returns server value, or 1.0 if not fetched yet
return QuestData.get_config("coin_multiplier", 1.0)
func should_show_ads() -> bool:
return QuestData.get_config("show_ads", true)
func get_daily_bonus() -> int:
return QuestData.get_config("daily_bonus", 100)

Power move: Create a Segment for VIP players (tag: vip) and give them coin_multiplier = 2.0. They get double coins without any code change.

See Remote Config Reference and the Live Ops Guide.

Add a global leaderboard so players can compete:

func _on_game_over(final_score: int):
# Submit score — SDK buffers and sends efficiently
QuestData.submit_score("global_highscore", final_score, {
"level_reached": current_level
}, player_display_name)
func show_leaderboard():
# Fetch top 10
QuestData.get_leaderboard("global_highscore", 10, func(entries: Array):
for entry in entries:
add_leaderboard_row(entry["rank"], entry["player_name"], entry["score"])
)

The SDK handles score buffering (only sends the highest score per 5-second window) and the server uses UPSERT logic (only updates if the new score is higher).

See Leaderboards Reference and the Player Management Guide.

Catch errors so you can debug production crashes remotely:

func load_save_file() -> Dictionary:
var file = FileAccess.open("user://save.dat", FileAccess.READ)
if file == null:
QuestData.track_error("SaveLoadFailed", "Could not open save file")
return {}
var data = file.get_var()
if not data is Dictionary:
QuestData.track_error("SaveCorrupted", "Expected Dictionary, got " + type_string(typeof(data)))
return {}
return data

The SDK automatically captures stack traces and 60 seconds of pre-crash telemetry (FPS, memory, recent events).

See Error Tracking Reference and the Remote Debugging Guide.

Your game now:

  • Tracks core events and milestones
  • Identifies players with properties
  • Measures progression (completion rates, duration, failure reasons)
  • Records revenue and purchase data
  • Reads live config from the server (no app update needed)
  • Has a global leaderboard
  • Catches errors with stack traces

All of this data flows into your dashboard in real-time. Open it, play your game for a minute, and watch the events appear.