Skip to main content

Finding the Best Region for a Player

GameFlow exposes lightweight ping endpoints for each region so game clients can measure latency and select the best region for a match.

Ping Endpoints

Each region has a dedicated ping endpoint that responds to HTTP GET requests. The response includes a 200 OK with minimal payload, making it ideal for round-trip time measurement.

RegionEndpoint
US Easthttps://us-east.ping.gameflow.gg
US Westhttps://us-west.ping.gameflow.gg
EU Centralhttps://eu-central.ping.gameflow.gg
AP Southeasthttps://ap-southeast.ping.gameflow.gg

Measuring Latency

Send an HTTP GET request to each endpoint and measure the round-trip time. Compare the results to determine the lowest-latency region for the player.

#include "HttpModule.h"
#include "Interfaces/IHttpRequest.h"
#include "Interfaces/IHttpResponse.h"

void UMySubsystem::PingRegion(const FString& Region)
{
FString Url = FString::Printf(TEXT("https://%s.ping.gameflow.gg"), *Region);

double StartTime = FPlatformTime::Seconds();

TSharedRef<IHttpRequest> Request = FHttpModule::Get().CreateRequest();
Request->SetURL(Url);
Request->SetVerb(TEXT("GET"));
Request->OnProcessRequestComplete().BindLambda(
[StartTime, Region](FHttpRequestPtr Req, FHttpResponsePtr Res, bool bSuccess)
{
if (bSuccess && Res.IsValid())
{
double Ms = (FPlatformTime::Seconds() - StartTime) * 1000.0;
UE_LOG(LogTemp, Log, TEXT("Ping to %s: %.0f ms"), *Region, Ms);
}
});
Request->ProcessRequest();
}

// Ping all regions
void UMySubsystem::PingAllRegions()
{
TArray<FString> Regions = {
TEXT("us-east"), TEXT("us-west"),
TEXT("eu-central"), TEXT("ap-southeast")
};
for (const FString& Region : Regions)
{
PingRegion(Region);
}
}

Using the Best Region

Once you've determined the best region, pass it when requesting a game server via the GameFlow API. There are two approaches depending on your setup.

Standalone Server

Start a one-off game server with POST /v1/games/:gameId/servers. GameFlow provisions the server and blocks until it's ready, returning its IP and port.

const bestRegion = (await pingAllRegions())[0].region;

const response = await fetch(
`https://api.gameflow.gg/v1/games/${gameId}/servers`,
{
method: "POST",
headers: { "X-Api-Key": apiKey },
body: JSON.stringify({
region: bestRegion,
payload: JSON.stringify({ players, teamA, teamB }),
}),
}
);

const { server } = await response.json();
// server.address, server.port

Fleet Allocation (Autoscaling)

If you have autoscaling configured via a fleet, allocate a pre-warmed server with POST /v1/fleets/:gameId/allocate. This returns an already-running server from the pool, so it's faster than starting a standalone server.

const bestRegion = (await pingAllRegions())[0].region;

const response = await fetch(
`https://api.gameflow.gg/v1/fleets/${gameId}/allocate`,
{
method: "POST",
headers: { "X-Api-Key": apiKey },
body: JSON.stringify({
region: bestRegion,
payload: JSON.stringify({ players, teamA, teamB }),
}),
}
);

const { allocation } = await response.json();
// allocation.address, allocation.port

See the Custom Game Backend guide for a full integration walkthrough.

Best Practices

  • Ping on game launch or lobby entry — avoid pinging mid-match to reduce unnecessary network traffic.
  • Let the player override — display the results and allow the player to manually select a region if they prefer.
  • Discard the first ping — the initial request includes TLS handshake overhead. Drop it and average the rest for a more accurate measurement.
  • Ping multiple times and average — a single request can be affected by cold connections or transient network conditions. Averaging 3-5 pings per region gives a more stable result.