Skip to content
BloxIQBloxIQ
Guides

How to save player data on Roblox without losing progress

A practical DataStoreService guide: what to save, UpdateAsync vs SetAsync, retries, saving on shutdown, session locking, request limits and erasure requests.

By BloxIQ Research · · 9 min read

Lost progress is the fastest way to lose a player for good. Someone who spent three evenings in your tycoon and comes back to an empty save rarely gives you a second chance, and they often leave a review saying so. Roblox's DataStoreService is reliable when it's used carefully. This guide covers the habits that keep saves safe, whatever genre you're making.

Turn on API access for testing

DataStores work in live servers by default. To test them in Studio, open Game Settings → Security and enable Studio access to API services. Be aware that Studio then reads and writes the same data as your live game, so use a separate test experience or a different DataStore name while developing.

Decide what to save

Save the smallest set of facts you need to rebuild the player's state:

  • Currencies and stats: cash, level, rebirths, stage.
  • Ownership: a list of item, pet or upgrade IDs, not the 3D parts themselves.
  • Settings: music volume, control preferences.
  • A version number, so you can migrate old saves when your format changes.

Rebuild everything else on join. Saving a description of the base ("owns conveyor 3, dropper 5") is far smaller and safer than trying to save the parts.

Use one key per player

The usual pattern is one DataStore for player data and one key per player, built from their UserId (for example "player_" followed by the UserId). Never use the player's name: names can change, UserIds can't.

Keep the whole record in a single table under that key. Each key can hold a few megabytes, which is far more than most games need.

Wrap every call in pcall and retry

DataStore calls go over the network, so they can fail. Every GetAsync, SetAsync and UpdateAsync should be wrapped in pcall, with a few retries and a short wait between them.

The most important rule is what to do when loading fails. If you can't load a player's data, don't let them play on a fresh, empty save, because when they leave you'll overwrite their real progress with that empty one. Either kick them with a friendly message asking them to rejoin, or mark the session as "don't save".

Prefer UpdateAsync to SetAsync

SetAsync overwrites whatever is there. UpdateAsync gives you the current saved value and lets you decide what to write, which makes it safer when two servers might touch the same key (for example when a player hops servers quickly). Use it to:

  • Refuse to overwrite a save with a higher version or newer timestamp than yours.
  • Merge changes instead of replacing the whole record.

Save at the right moments

  • On a timer, every few minutes, so a crash doesn't lose a whole session.
  • When the player leaves, in Players.PlayerRemoving.
  • When the server shuts down, using game:BindToClose. When a server closes (for example after an update), PlayerRemoving might not finish in time, so save every remaining player there and wait for the saves to complete.
  • After important events, such as a purchase, but throttled so you don't save on every coin.

Respect request limits

Each server has a budget of DataStore requests per minute that grows with the number of players. Saving on every coin pickup will burn through it and start queueing or failing requests. Keep a player's data in a table in memory while they play, change that table freely, and only write it to the DataStore at the moments above. Roblox's data store documentation lists the current limits.

Session locking for bigger games

If players can leave one server and join another within seconds, the second server might load old data before the first server's save finishes. That's how items get duplicated or progress rolls back. Session locking solves this by marking a save as "in use" by one server until it's released. You can build it with UpdateAsync, but for most creators a well-tested community library such as ProfileStore is a better choice than writing your own.

Purchases and saves

Developer product purchases should only be confirmed after the grant has been saved. If your save fails, return NotProcessedYet so Roblox retries later. See game passes vs developer products for the full receipt pattern.

Erasure requests

Roblox sends creators requests to delete data for specific users under privacy laws (they arrive as messages to the experience owner). Keep your keys based on UserId so you can find and remove that user's data with RemoveAsync, including any extra data stores such as leaderboards.

Test it like a player would

Before launch, play the published game and try to break your saves:

  • Join, earn something, leave, rejoin.
  • Leave while a purchase is processing.
  • Shut down servers from the Creator Hub while you're in the game.
  • Join two servers in quick succession.

The best time to update your Roblox game covers restarting servers without losing players, which depends on BindToClose working.

Checklist

  • Studio API access on only for testing; separate test data store
  • Minimal record per UserId with a version number
  • pcall with retries; failed loads never start an empty save
  • UpdateAsync for writes that could race
  • Save on a timer, on leave, on BindToClose and after purchases
  • In-memory changes, few writes; request limits respected
  • Session locking or a proven library for busy games
  • Erasure requests handled

Reliable saves are part of retention. If your Creator Analytics show players not coming back after day one, BloxIQ's AI Coach can help you work out whether it's the first session, the loop or something more basic like lost progress.

Spotted an error? Tell us and we'll correct it. BloxIQ is not affiliated with Roblox Corporation.

Keep reading

Put the habits into practice

BloxIQ tracks medians, coverage and baselines for you.

How to save player data on Roblox without losing progress · BloxIQ