Hello! My name is Sergey Kachan, and I’m a client developer on the War Robots project. War Robots has been around for many years, and during this time the game has accumulated a huge variety of content: robots, weapons, drones, titans, pilots, and so on. And for all of this to work, we need to store a large amount of different types of information. This information is stored in “balances.” Today I’m going to talk about how balances are structured in our project, what’s happened to them over the past 11 years, and how we’ve dealt with it. Balances in the Project Like any other project, War Robots can be divided into two parts: meta and core gameplay. Meta gameplay (metagaming) is any activity that goes beyond the core game loop but still affects the gameplay. This includes purchasing and upgrading game content, participating in social or event activities. Core gameplay (core gameplay loop) is the main repeating cycle of actions that the player performs in the game to achieve their goals. In our case, it’s robot battles on specific maps. Each part of the project needs its own balance, so we also split balances into two categories — meta and core. War Robots also has so-called Skirmish modes, which require their own separate balances. A Skirmish mode is a modification of existing modes or maps with different characteristics or rules. Skirmish modes are often event-based, available to players during various holidays, mainly for fun. For example, players might be able to kill each other with a single shot or move around in zero gravity. So in total, we have 4 balances: 2 for the default mode and 2 for the Skirmish mode. \ \ Over 11 years, War Robots has accumulated a ton of awesome content: 95 robots 21 titans 175 different weapons 40 drones 16 motherships a huge number of skins, remodels, modules, pilots, turrets, ultimate versions of content, and maps And as you can imagine, to make all of this work we need to store information about behavior, stats, availability, prices, and much, much more. As a result, our balances have grown to an indecent size: | \ | Default mode | Skirmish mode | |----|----|----| | Meta balance | 9.2 MB | 9.2 MB | | Core balance | 13.1 MB | 13.1 MB | After some quick calculations, we found that a player would need to download 44.6 MB. That’s quite a lot! We really didn’t want to force players to download such large amounts of data every time a balance changed. And distributing that much data via CDN isn’t exactly cheap either. Just to remind you: War Robots has reached 300 million registered users. In 2024, our monthly active audience was 4.7 million people, and 690 thousand players logged in every day. Now imagine the amount of data. A lot, right? We thought so too. So, we decided to do everything we could to cut down the size of our balances! Hunting Down the Problem The first step was to analyze the balances and try to figure out: “What’s taking up so much space?” Manually going through everything was the last thing we wanted to do — it would’ve taken ages. So, we wrote a set of tools that collected and aggregated all the information we needed about the balances. The tool would take a balance file as input and, using reflection, iterate through all the structures, gathering data on what types of information we stored and how much space each one occupied. The results were discouraging: Meta Balance | \ | % in balance | Usage count | |----|----|----| | String | 28.478 % | 164 553 | | Int32 | 27.917 % | 161 312 | | Boolean | 6.329 % | 36 568 | | Double | 5.845 % | 33 772 | | Int64 | 4.682 % | 27 054 | | Custom structures | 26.749 % | — | \ Core Balance | \ | % in balance | Usage count | |----|----|----| | String | 34.259 % | 232 229 | | Double | 23.370 % | 158 418 | | Int32 | 20.955 % | 142 050 | | Boolean | 5.306 % | 34 323 | | Custom structures | 16.11 % | — | \ After analyzing the situation, we realized that strings were taking up far too much space, and something had to be done about it. So, we built another tool. This one scanned the balance file and generated a map of all the strings along with the number of times each one was duplicated. The results weren’t encouraging either. Some strings were repeated tens of thousands of times! We had found the problem. Now the question was: how do we fix it? Optimizing the Balances For obvious reasons, we couldn’t just get rid of strings altogether. Strings are used for things like localization keys and various IDs. But what we could do was eliminate the duplication of strings. The idea was as simple as it gets: Create a list of unique strings for each balance (essentially, a dedicated storage). Send this list along with the data. \ public class BalanceMessage {   public BalanceMessageData Data;   public StringStorage Storage;   public string Version; } \ StringStorage is essentially a wrapper around a list of strings. When we build the string storage, each balance structure remembers the index of the string it needs. Later, when retrieving data, we just pass the index and quickly get the value. \ public class StringStorage {    public List<string> Values;    public string GetValue(StringIdx id) => Values[id]; } \ Instead of passing the strings themselves inside the balance structures, we began passing the index of where the string is stored in the string storage. Before: \ public class SomeBalanceMessage {   public string Id;   public string Name;   public int Amount; } \ After: \ public class SomeBalanceMessageV2 {   public StringIdx Id;   public StringIdx Name;   public int Amount; } \ StringIdx is basically just a wrapper around an int. This way, we completely eliminated direct string transfers inside the balance structures. \ public readonly struct StringIdx : IEquatable<StringIdx> {   private readonly int _id;   internal StringIdx(int value) {_id = value; }   public static implicit operator int(StringIdx value) => value._id;  public bool Equals(StringIdx other) => _id == other._id; } \ This approach reduced the number of strings by tens of times. \ | \ | String usage count | String usage count | |----|----|----| | | Before | After | | Meta balance | 164 553 | 10 082 | | Core balance | 232 229 | 14 228 | Not bad, right? But that was just the beginning — we didn’t stop there. Reworking the Data Protocol For transmitting and processing balance structures, we had been using MessagePack. MessagePack is a binary data serialization format designed as a more compact and faster alternative to JSON. It’s meant for efficient data exchange between applications or services, allowing a significant reduction in data size — especially useful where performance and bandwidth matter. Initially, MessagePack came in a JSON-like format, where the data used string keys. That’s certainly convenient, but also quite space-consuming. So we decided to sacrifice some flexibility and switch to a binary byte array. Before: \ public class SomeBalanceMessage {   [Key("id")]   public string Id;      [Key("name")]   public string Name;      [Key("amount")]   public int Amount; } \ After: \ public class SomeBalanceMessageV2 {   [Key(0)]   public StringIdx Id;      [Key(1)]   public StringIdx Name;      [Key(2)]   public int Amount; } \ We also removed all empty collections — instead of sending them, we now transmit null values. This reduced both the overall data size and the time required for serialization and deserialization. Testing the Changes A golden rule of good development (and one that will save you a lot of nerves) is to always implement new features in a way that lets you quickly roll them back if something goes wrong. For that reason, we add all new features behind “toggles.” To make this work, we had to support two versions of balances at the same time: the old one and the optimized one. During development, we needed to make sure that all data was transferred correctly. Old and new balances — regardless of format or structure — had to produce the exact same values. And remember, the optimized balances had changed their structure drastically, but that wasn’t supposed to affect anything except their size. To achieve this, we wrote a large number of unit tests for each balance. At first, we compared all fields “head-on” — checking every single one explicitly. This worked, but it was time-consuming, and even the smallest change in the balances would break the tests, forcing us to rewrite them constantly. This slowed us down and was quite distracting. Eventually, we had enough of that and came up with a more convenient testing approach for comparing balances. Reflection came to the rescue again. We took two versions of the balance structures, e.g. SomeBalanceMessage and SomeBalanceMessageV2, and iterated over them — comparing field counts, names, and values. If anything didn’t match, we tracked down the problem. This solution saved us a huge amount of time later on. Optimization Results Thanks to these optimizations, we managed to reduce both the size of the files transmitted over the network and the time it takes to deserialize them on the client. We also decreased the amount of memory required on the client side after balance deserialization. File Size | \ | Old balances | Optimized balances | Profit | |----|----|----|----| | Meta balance | 9.2 MB | 1.28 MB | - 86 % | | Core balance | 13.1 MB | 2.22 MB | - 83 % | Deserialization Time | \ | Old balances | Optimized balances | Profit | |----|----|----|----| | Meta balance | 967 ms | 199 ms | - 79 % | | Core balance | 1165 ms | 265 ms | - 77 % | Data in Memory | \ | Old balances | Optimized balances | Profit | |----|----|----|----| | Meta + Core | ~ 45.3 MB | ~ 33.5 MB | - 26 % | Conclusions The results of the optimization fully satisfied us. The balance files were reduced by more than 80%. Traffic went down, and the players were happy. To sum it up: be careful with the data you transmit, and don’t send anything unnecessary. Strings are best stored in unique storages to avoid creating duplicates. And if your custom data (prices, stats, etc.) also contains a lot of repetition, try packing those into unique storages as well. This will save you many megabytes — and a lot of money on maintaining CDN serversHello! My name is Sergey Kachan, and I’m a client developer on the War Robots project. War Robots has been around for many years, and during this time the game has accumulated a huge variety of content: robots, weapons, drones, titans, pilots, and so on. And for all of this to work, we need to store a large amount of different types of information. This information is stored in “balances.” Today I’m going to talk about how balances are structured in our project, what’s happened to them over the past 11 years, and how we’ve dealt with it. Balances in the Project Like any other project, War Robots can be divided into two parts: meta and core gameplay. Meta gameplay (metagaming) is any activity that goes beyond the core game loop but still affects the gameplay. This includes purchasing and upgrading game content, participating in social or event activities. Core gameplay (core gameplay loop) is the main repeating cycle of actions that the player performs in the game to achieve their goals. In our case, it’s robot battles on specific maps. Each part of the project needs its own balance, so we also split balances into two categories — meta and core. War Robots also has so-called Skirmish modes, which require their own separate balances. A Skirmish mode is a modification of existing modes or maps with different characteristics or rules. Skirmish modes are often event-based, available to players during various holidays, mainly for fun. For example, players might be able to kill each other with a single shot or move around in zero gravity. So in total, we have 4 balances: 2 for the default mode and 2 for the Skirmish mode. \ \ Over 11 years, War Robots has accumulated a ton of awesome content: 95 robots 21 titans 175 different weapons 40 drones 16 motherships a huge number of skins, remodels, modules, pilots, turrets, ultimate versions of content, and maps And as you can imagine, to make all of this work we need to store information about behavior, stats, availability, prices, and much, much more. As a result, our balances have grown to an indecent size: | \ | Default mode | Skirmish mode | |----|----|----| | Meta balance | 9.2 MB | 9.2 MB | | Core balance | 13.1 MB | 13.1 MB | After some quick calculations, we found that a player would need to download 44.6 MB. That’s quite a lot! We really didn’t want to force players to download such large amounts of data every time a balance changed. And distributing that much data via CDN isn’t exactly cheap either. Just to remind you: War Robots has reached 300 million registered users. In 2024, our monthly active audience was 4.7 million people, and 690 thousand players logged in every day. Now imagine the amount of data. A lot, right? We thought so too. So, we decided to do everything we could to cut down the size of our balances! Hunting Down the Problem The first step was to analyze the balances and try to figure out: “What’s taking up so much space?” Manually going through everything was the last thing we wanted to do — it would’ve taken ages. So, we wrote a set of tools that collected and aggregated all the information we needed about the balances. The tool would take a balance file as input and, using reflection, iterate through all the structures, gathering data on what types of information we stored and how much space each one occupied. The results were discouraging: Meta Balance | \ | % in balance | Usage count | |----|----|----| | String | 28.478 % | 164 553 | | Int32 | 27.917 % | 161 312 | | Boolean | 6.329 % | 36 568 | | Double | 5.845 % | 33 772 | | Int64 | 4.682 % | 27 054 | | Custom structures | 26.749 % | — | \ Core Balance | \ | % in balance | Usage count | |----|----|----| | String | 34.259 % | 232 229 | | Double | 23.370 % | 158 418 | | Int32 | 20.955 % | 142 050 | | Boolean | 5.306 % | 34 323 | | Custom structures | 16.11 % | — | \ After analyzing the situation, we realized that strings were taking up far too much space, and something had to be done about it. So, we built another tool. This one scanned the balance file and generated a map of all the strings along with the number of times each one was duplicated. The results weren’t encouraging either. Some strings were repeated tens of thousands of times! We had found the problem. Now the question was: how do we fix it? Optimizing the Balances For obvious reasons, we couldn’t just get rid of strings altogether. Strings are used for things like localization keys and various IDs. But what we could do was eliminate the duplication of strings. The idea was as simple as it gets: Create a list of unique strings for each balance (essentially, a dedicated storage). Send this list along with the data. \ public class BalanceMessage {   public BalanceMessageData Data;   public StringStorage Storage;   public string Version; } \ StringStorage is essentially a wrapper around a list of strings. When we build the string storage, each balance structure remembers the index of the string it needs. Later, when retrieving data, we just pass the index and quickly get the value. \ public class StringStorage {    public List<string> Values;    public string GetValue(StringIdx id) => Values[id]; } \ Instead of passing the strings themselves inside the balance structures, we began passing the index of where the string is stored in the string storage. Before: \ public class SomeBalanceMessage {   public string Id;   public string Name;   public int Amount; } \ After: \ public class SomeBalanceMessageV2 {   public StringIdx Id;   public StringIdx Name;   public int Amount; } \ StringIdx is basically just a wrapper around an int. This way, we completely eliminated direct string transfers inside the balance structures. \ public readonly struct StringIdx : IEquatable<StringIdx> {   private readonly int _id;   internal StringIdx(int value) {_id = value; }   public static implicit operator int(StringIdx value) => value._id;  public bool Equals(StringIdx other) => _id == other._id; } \ This approach reduced the number of strings by tens of times. \ | \ | String usage count | String usage count | |----|----|----| | | Before | After | | Meta balance | 164 553 | 10 082 | | Core balance | 232 229 | 14 228 | Not bad, right? But that was just the beginning — we didn’t stop there. Reworking the Data Protocol For transmitting and processing balance structures, we had been using MessagePack. MessagePack is a binary data serialization format designed as a more compact and faster alternative to JSON. It’s meant for efficient data exchange between applications or services, allowing a significant reduction in data size — especially useful where performance and bandwidth matter. Initially, MessagePack came in a JSON-like format, where the data used string keys. That’s certainly convenient, but also quite space-consuming. So we decided to sacrifice some flexibility and switch to a binary byte array. Before: \ public class SomeBalanceMessage {   [Key("id")]   public string Id;      [Key("name")]   public string Name;      [Key("amount")]   public int Amount; } \ After: \ public class SomeBalanceMessageV2 {   [Key(0)]   public StringIdx Id;      [Key(1)]   public StringIdx Name;      [Key(2)]   public int Amount; } \ We also removed all empty collections — instead of sending them, we now transmit null values. This reduced both the overall data size and the time required for serialization and deserialization. Testing the Changes A golden rule of good development (and one that will save you a lot of nerves) is to always implement new features in a way that lets you quickly roll them back if something goes wrong. For that reason, we add all new features behind “toggles.” To make this work, we had to support two versions of balances at the same time: the old one and the optimized one. During development, we needed to make sure that all data was transferred correctly. Old and new balances — regardless of format or structure — had to produce the exact same values. And remember, the optimized balances had changed their structure drastically, but that wasn’t supposed to affect anything except their size. To achieve this, we wrote a large number of unit tests for each balance. At first, we compared all fields “head-on” — checking every single one explicitly. This worked, but it was time-consuming, and even the smallest change in the balances would break the tests, forcing us to rewrite them constantly. This slowed us down and was quite distracting. Eventually, we had enough of that and came up with a more convenient testing approach for comparing balances. Reflection came to the rescue again. We took two versions of the balance structures, e.g. SomeBalanceMessage and SomeBalanceMessageV2, and iterated over them — comparing field counts, names, and values. If anything didn’t match, we tracked down the problem. This solution saved us a huge amount of time later on. Optimization Results Thanks to these optimizations, we managed to reduce both the size of the files transmitted over the network and the time it takes to deserialize them on the client. We also decreased the amount of memory required on the client side after balance deserialization. File Size | \ | Old balances | Optimized balances | Profit | |----|----|----|----| | Meta balance | 9.2 MB | 1.28 MB | - 86 % | | Core balance | 13.1 MB | 2.22 MB | - 83 % | Deserialization Time | \ | Old balances | Optimized balances | Profit | |----|----|----|----| | Meta balance | 967 ms | 199 ms | - 79 % | | Core balance | 1165 ms | 265 ms | - 77 % | Data in Memory | \ | Old balances | Optimized balances | Profit | |----|----|----|----| | Meta + Core | ~ 45.3 MB | ~ 33.5 MB | - 26 % | Conclusions The results of the optimization fully satisfied us. The balance files were reduced by more than 80%. Traffic went down, and the players were happy. To sum it up: be careful with the data you transmit, and don’t send anything unnecessary. Strings are best stored in unique storages to avoid creating duplicates. And if your custom data (prices, stats, etc.) also contains a lot of repetition, try packing those into unique storages as well. This will save you many megabytes — and a lot of money on maintaining CDN servers

Optimizing the Ever-Growing Balance in an 11-Year-Old Game

9 min read

Hello! My name is Sergey Kachan, and I’m a client developer on the War Robots project.

War Robots has been around for many years, and during this time the game has accumulated a huge variety of content: robots, weapons, drones, titans, pilots, and so on. And for all of this to work, we need to store a large amount of different types of information. This information is stored in “balances.”

Today I’m going to talk about how balances are structured in our project, what’s happened to them over the past 11 years, and how we’ve dealt with it.

Balances in the Project

Like any other project, War Robots can be divided into two parts: meta and core gameplay.

Meta gameplay (metagaming) is any activity that goes beyond the core game loop but still affects the gameplay. This includes purchasing and upgrading game content, participating in social or event activities.

Core gameplay (core gameplay loop) is the main repeating cycle of actions that the player performs in the game to achieve their goals. In our case, it’s robot battles on specific maps.

Each part of the project needs its own balance, so we also split balances into two categories — meta and core.

War Robots also has so-called Skirmish modes, which require their own separate balances.

A Skirmish mode is a modification of existing modes or maps with different characteristics or rules. Skirmish modes are often event-based, available to players during various holidays, mainly for fun. For example, players might be able to kill each other with a single shot or move around in zero gravity.

So in total, we have 4 balances: 2 for the default mode and 2 for the Skirmish mode.

\

\ Over 11 years, War Robots has accumulated a ton of awesome content:

  • 95 robots
  • 21 titans
  • 175 different weapons
  • 40 drones
  • 16 motherships
  • a huge number of skins, remodels, modules, pilots, turrets, ultimate versions of content, and maps

And as you can imagine, to make all of this work we need to store information about behavior, stats, availability, prices, and much, much more.

As a result, our balances have grown to an indecent size:

| \ | Default mode | Skirmish mode | |----|----|----| | Meta balance | 9.2 MB | 9.2 MB | | Core balance | 13.1 MB | 13.1 MB |

After some quick calculations, we found that a player would need to download 44.6 MB. That’s quite a lot!

We really didn’t want to force players to download such large amounts of data every time a balance changed. And distributing that much data via CDN isn’t exactly cheap either.

Just to remind you: War Robots has reached 300 million registered users. In 2024, our monthly active audience was 4.7 million people, and 690 thousand players logged in every day.

Now imagine the amount of data. A lot, right? We thought so too. So, we decided to do everything we could to cut down the size of our balances!

Hunting Down the Problem

The first step was to analyze the balances and try to figure out: “What’s taking up so much space?”

Manually going through everything was the last thing we wanted to do — it would’ve taken ages. So, we wrote a set of tools that collected and aggregated all the information we needed about the balances.

The tool would take a balance file as input and, using reflection, iterate through all the structures, gathering data on what types of information we stored and how much space each one occupied.

The results were discouraging:

Meta Balance

| \ | % in balance | Usage count | |----|----|----| | String | 28.478 % | 164 553 | | Int32 | 27.917 % | 161 312 | | Boolean | 6.329 % | 36 568 | | Double | 5.845 % | 33 772 | | Int64 | 4.682 % | 27 054 | | Custom structures | 26.749 % | — |

\

Core Balance

| \ | % in balance | Usage count | |----|----|----| | String | 34.259 % | 232 229 | | Double | 23.370 % | 158 418 | | Int32 | 20.955 % | 142 050 | | Boolean | 5.306 % | 34 323 | | Custom structures | 16.11 % | — |

\ After analyzing the situation, we realized that strings were taking up far too much space, and something had to be done about it.

So, we built another tool. This one scanned the balance file and generated a map of all the strings along with the number of times each one was duplicated.

The results weren’t encouraging either. Some strings were repeated tens of thousands of times!

We had found the problem. Now the question was: how do we fix it?

Optimizing the Balances

For obvious reasons, we couldn’t just get rid of strings altogether. Strings are used for things like localization keys and various IDs. But what we could do was eliminate the duplication of strings.

The idea was as simple as it gets:

  • Create a list of unique strings for each balance (essentially, a dedicated storage).
  • Send this list along with the data.

\

public class BalanceMessage {   public BalanceMessageData Data;   public StringStorage Storage;   public string Version; } 

\ StringStorage is essentially a wrapper around a list of strings. When we build the string storage, each balance structure remembers the index of the string it needs. Later, when retrieving data, we just pass the index and quickly get the value.

\

public class StringStorage { &nbsp;&nbsp;&nbsp;public List<string> Values; &nbsp;&nbsp;&nbsp;public string GetValue(StringIdx id) => Values[id]; } 

\ Instead of passing the strings themselves inside the balance structures, we began passing the index of where the string is stored in the string storage.

Before:

\

public class SomeBalanceMessage { &nbsp;&nbsp;public string Id; &nbsp;&nbsp;public string Name; &nbsp;&nbsp;public int Amount; } 

\ After:

\

public class SomeBalanceMessageV2 { &nbsp;&nbsp;public StringIdx Id; &nbsp;&nbsp;public StringIdx Name; &nbsp;&nbsp;public int Amount; } 

\ StringIdx is basically just a wrapper around an int. This way, we completely eliminated direct string transfers inside the balance structures.

\

public readonly struct StringIdx : IEquatable<StringIdx> { &nbsp;&nbsp;private readonly int _id; &nbsp;&nbsp;internal StringIdx(int value) {_id = value; } &nbsp;&nbsp;public static implicit operator int(StringIdx value) => value._id; &nbsp;public bool Equals(StringIdx other) => _id == other._id; } 

\ This approach reduced the number of strings by tens of times.

\

| \ | String usage count | String usage count | |----|----|----| | | Before | After | | Meta balance | 164 553 | 10 082 | | Core balance | 232 229 | 14 228 |

Not bad, right?

But that was just the beginning — we didn’t stop there.

Reworking the Data Protocol

For transmitting and processing balance structures, we had been using MessagePack.

MessagePack is a binary data serialization format designed as a more compact and faster alternative to JSON. It’s meant for efficient data exchange between applications or services, allowing a significant reduction in data size — especially useful where performance and bandwidth matter.

Initially, MessagePack came in a JSON-like format, where the data used string keys. That’s certainly convenient, but also quite space-consuming. So we decided to sacrifice some flexibility and switch to a binary byte array.

Before:

\

public class SomeBalanceMessage { &nbsp;&nbsp;[Key("id")] &nbsp;&nbsp;public string Id; &nbsp;&nbsp; &nbsp;&nbsp;[Key("name")] &nbsp;&nbsp;public string Name; &nbsp;&nbsp; &nbsp;&nbsp;[Key("amount")] &nbsp;&nbsp;public int Amount; } 

\ After:

\

public class SomeBalanceMessageV2 { &nbsp;&nbsp;[Key(0)] &nbsp;&nbsp;public StringIdx Id; &nbsp;&nbsp; &nbsp;&nbsp;[Key(1)] &nbsp;&nbsp;public StringIdx Name; &nbsp;&nbsp; &nbsp;&nbsp;[Key(2)] &nbsp;&nbsp;public int Amount; } 

\ We also removed all empty collections — instead of sending them, we now transmit null values. This reduced both the overall data size and the time required for serialization and deserialization.

Testing the Changes

A golden rule of good development (and one that will save you a lot of nerves) is to always implement new features in a way that lets you quickly roll them back if something goes wrong. For that reason, we add all new features behind “toggles.” To make this work, we had to support two versions of balances at the same time: the old one and the optimized one.

During development, we needed to make sure that all data was transferred correctly. Old and new balances — regardless of format or structure — had to produce the exact same values. And remember, the optimized balances had changed their structure drastically, but that wasn’t supposed to affect anything except their size.

To achieve this, we wrote a large number of unit tests for each balance.

At first, we compared all fields “head-on” — checking every single one explicitly. This worked, but it was time-consuming, and even the smallest change in the balances would break the tests, forcing us to rewrite them constantly. This slowed us down and was quite distracting.

Eventually, we had enough of that and came up with a more convenient testing approach for comparing balances.

Reflection came to the rescue again. We took two versions of the balance structures, e.g. SomeBalanceMessage and SomeBalanceMessageV2, and iterated over them — comparing field counts, names, and values. If anything didn’t match, we tracked down the problem. This solution saved us a huge amount of time later on.

Optimization Results

Thanks to these optimizations, we managed to reduce both the size of the files transmitted over the network and the time it takes to deserialize them on the client. We also decreased the amount of memory required on the client side after balance deserialization.

File Size

| \ | Old balances | Optimized balances | Profit | |----|----|----|----| | Meta balance | 9.2 MB | 1.28 MB | - 86 % | | Core balance | 13.1 MB | 2.22 MB | - 83 % |

Deserialization Time

| \ | Old balances | Optimized balances | Profit | |----|----|----|----| | Meta balance | 967 ms | 199 ms | - 79 % | | Core balance | 1165 ms | 265 ms | - 77 % |

Data in Memory

| \ | Old balances | Optimized balances | Profit | |----|----|----|----| | Meta + Core | ~ 45.3 MB | ~ 33.5 MB | - 26 % |

Conclusions

The results of the optimization fully satisfied us. The balance files were reduced by more than 80%. Traffic went down, and the players were happy.

To sum it up: be careful with the data you transmit, and don’t send anything unnecessary.

Strings are best stored in unique storages to avoid creating duplicates. And if your custom data (prices, stats, etc.) also contains a lot of repetition, try packing those into unique storages as well. This will save you many megabytes — and a lot of money on maintaining CDN servers.

Market Opportunity
SQUID MEME Logo
SQUID MEME Price(GAME)
$41.7997
$41.7997$41.7997
-1.27%
USD
SQUID MEME (GAME) Live Price Chart
Disclaimer: The articles reposted on this site are sourced from public platforms and are provided for informational purposes only. They do not necessarily reflect the views of MEXC. All rights remain with the original authors. If you believe any content infringes on third-party rights, please contact [email protected] for removal. MEXC makes no guarantees regarding the accuracy, completeness, or timeliness of the content and is not responsible for any actions taken based on the information provided. The content does not constitute financial, legal, or other professional advice, nor should it be considered a recommendation or endorsement by MEXC.

You May Also Like

Next Couple of Months Will Be Wild for XRP: Wealth Manager

Next Couple of Months Will Be Wild for XRP: Wealth Manager

Wealth manager Nate Geraci has reiterated his view that the coming months will be "wild" for XRP. This comes as the XRP community braces for another major milestone in the ETF space.Visit Website
Share
The Crypto Basic2025/09/18 17:09
Top 5 Cryptocurrency Data APIs: Comprehensive Comparison (2025)

Top 5 Cryptocurrency Data APIs: Comprehensive Comparison (2025)

Photo by Pierre Borthiry - Peiobty on Unsplash Cryptocurrency APIs are essential tools for developers building apps (e.g. trading bots, portfolio trackers) and for analysts conducting market research. These APIs provide programmatic access to historical price data, real-time market quotes, and even on-chain metrics from blockchain networks. Choosing the right API means finding a balance between data coverage, update speed, reliability, and cost. In this article, we compare five of the most popular crypto data API providers — EODHD, CoinMarketCap, CoinGecko, CryptoCompare, and Glassnode — focusing on their features, data types (historical, real-time, on-chain), rate limits, documentation, and pricing plans. We also highlight where EODHD’s crypto API stands out in this competitive landscape. Overview of the Top 5 Crypto Data API Providers
  1. EODHD (End-of-Day Historical Data) — All-in-One Multi-Asset Data EODHD is a versatile financial data provider covering stocks, forex, and cryptocurrencies. It offers an unmatched data coverage with up to 30 years of historical data across the global For crypto, EODHD supports thousands of coins and trading pairs (2,600+ crypto pairs against USD) and provides multiple data types under one service. Key features include:
Historical Price Data: Daily OHLCV (open-high-low-close-volume) for crypto assets, with records for major coins going back to 2009 eodhd.com (essentially as far back as Bitcoin’s history). This extensive archive facilitates long-term backtesting. Real-Time Market Data: Live crypto price quotes via REST API and WebSocket. EODHD’s “Live” plan delivers real-time (typically streaming) updates with high rate limits (up to 1,000 requests/minute on paid plans) Developers can also use bulk API endpoints to On-Chain & Fundamental Data: While not an on-chain analytics platform per se, EODHD provides crypto fundamental metrics such as market cap (actual and diluted), circulating/total/max supply, all-time high/low, and links to each project’s whitepaper, block explorer These fundamentals give context beyond price, though advanced on-chain metrics (e.g. active addresses) are not included. Additional Features: EODHD stands out for its ease of use and support tools. API responses are clean JSON by default (with an option for CSV), and the service offers no-code solutions like Excel and Google Sheets add-ons to fetch crypto data without programming Comprehensive documentation and an “API Academy” with examples help users get started EODHD also provides 24/7 live customer support, reflecting its 7+ years of reliable service Pricing & Limits: EODHD’s pricing is very competitive for the value. It has a free plan (registration required) which allows 20 API calls per day for trying out basic Paid plans start at $19.99/month for end-of-day and live crypto data, allowing up to 100,000 calls per day— a generous limit that far exceeds most competitors at that price. The next tier ($29.99/mo) adds real-time WebSocket streaming, and the top All-in-One plan ($99.99/mo) unlocks everything (historical, intraday, real-time, fundamentals, news, etc.) All paid plans come with high throughput (up to 1,000 requests/min) Enterprise or commercial licenses are available for custom needs, and students can even get 50% discounts for educational Overall, EODHD offers an excellent price-to-performance ratio, giving developers extensive crypto (and cross-asset) data for a fraction of the cost of some single-purpose crypto APIs. 2. CoinMarketCap — Industry-Standard Market Data CoinMarketCap (CMC) is one of the most well-known cryptocurrency data aggregators. It provides information on over 10,000 digital assets and aggregates data from hundreds of CMC’s API is a go-to choice for current market prices, rankings, and exchange statistics. Key features include: Real-Time Quotes & Global Metrics: The API offers real-time price quotes, market capitalization, trading volume, and rankings for thousands of cryptocurrencies. It also provides global market metrics like total market cap, total volume, Bitcoin dominance, etc., updated (CMC’s data updates roughly every 1–2 minutes by default; true streaming is not yet available via their API.) Historical Data: Paid tiers unlock access to historical price data. CMC has data going back to 2013 for many assets, and enterprise plans provide all historical OHLCV data since 2013.The API endpoints include daily and even intraday historical quotes, but note that the free tier does not include historical price retrieval(free users get only latest data). Exchange and Market Endpoints: CoinMarketCap’s API covers exchange-level data (e.g. exchange listings, trading pair metadata, liquidity scores) and derivative market data (futures, options prices) on higher plans. This is useful for monitoring exchange performance and volumes across both centralized and decentralized exchanges. However, on-chain analytics are not CMC’s focus — the API doesn’t provide blockchain metrics like address counts or transaction rates. Developer Support: CMC provides comprehensive documentation and a straightforward RESTful JSON API . The endpoints are well-documented with examples, and categories include latest listings, historical quotes, metadata/info (project details), exchange stats, and The service is known for its reliability and is used by major companies (Yahoo Finance, for example, uses CoinMarketCap’s data feeds in its crypto Pricing & Limits: CoinMarketCap offers a free Basic plan with 10,000 credits per month (approximately 333 calls/day) and access to 11 core endpoint. The free tier is suitable for simple apps that only need current market data on a limited number of assets. To get historical data or higher frequency updates, you must upgrade. The Hobbyist plan starts at around $29/month (paid annually) and offers a higher monthly call allowance (e.g. ~50,000 calls/month) and more endpoints. Mid-tier plans like Startup ($79/mo) and Standard ($199/mo) increase the rate limits and data access — e.g., more historical data and additional endpoints like derivatives or exchange listings. For example, Standard and above allow intraday historical quotes and more frequent updates. Professional/Enterprise plans ($699/mo and up, or custom) provide the highest limits (up to millions of calls per month), full historical datasets, and SLA . Rate limits on CMC are enforced via a credit system; different endpoints consume different credits, and higher plans simply grant more credits per month. In summary, CoinMarketCap’s API is very robust but can become expensive for extensive data needs — it targets enterprise use cases with its upper tiers. Smaller developers often stick to the free or Hobbyist plan for basic data (while accepting the lack of historical data in those tiers) 3. CoinGecko — Broad Coverage & Community Focus CoinGecko is another hugely popular cryptocurrency data provider known for its broad coverage and developer-friendly approach. CoinGecko’s API is often praised for having a useful free offering and covering not just standard market data but also categories like DeFi, NFTs, and community metrics. Notable features: Wide Asset Coverage: CoinGecko tracks over 13,000 cryptocurrencies (including many small-cap and emerging tokens). It also includes data on NFT collections and decentralized finance (DeFi) tokens and protocols. This makes it one of the most comprehensive datasets for the crypto market. If an asset is trading on a major exchange or DEX, CoinGecko likely has it listed. Market Data and Beyond: The API provides real-time price data, market caps, volumes, and historical charts for all these assets. Historical data can be retrieved in the form of market charts (typically with daily or hourly granularity depending on the time range). Additionally, CoinGecko offers endpoints for exchange data, trading pairs, categories (sectors), indices, and even asset contract info (mapping contract addresses to CoinGecko listings). They also expose developer and social metrics for each coin — e.g. GitHub repo stats (forks, stars, commits) and social media stats (Twitter followers, Reddit subscribers) This is valuable for analysts who want to gauge community interest or development activity alongside price. No WebSockets — REST Only: CoinGecko’s API is purely REST-based; there is no built-in WebSocket streaming. Data updates for price endpoints are cached at intervals (typically every 1–5 minutes for free users, and up to every 30 seconds for Pro users). So while you can get near-real-time data by polling, ultra-low-latency needs (like high-frequency trading) are better served by other providers or exchange-specific APIs. Documentation & Use: The API is very straightforward to use — in fact, for the free tier no API key was required historically (though recently CoinGecko introduced an optional “Demo” key for better tracking). A simple GET request to an endpoint like /simple/price returns current prices. CoinGecko’s documentation is clear, and they even highlight popular endpoints and provide examples. Because of its simplicity and generous free limits, CoinGecko’s API has been integrated into countless projects and tutorials. Pricing & Limits: CoinGecko operates a freemium model. The free tier (now referred to as the “Demo” plan) allows about 10–30 calls per minute (the exact rate is dynamic based on system load) In practical terms, that’s roughly up to 1,800 calls/hour if usage is maxed out — very sufficient for small applications. The free API gives access to most endpoints and data (including historical market charts) but with lower priority and slower update frequency. For higher needs, CoinGecko offers paid plans: Analyst, Lite, and Pro. For example, the Analyst plan (~$129/mo) offers 500,000 calls per month at 500 calls/minute rate limit, the Pro plan (~$499/mo) offers 2,000,000 calls/mo at the same rate, and an Enterprise plan (~$999/mo and up) can be tailored for even larger volumes. Paid plans also use a separate pro API endpoint with faster data updates (prices cached every 30 seconds) and come with commercial usage rights and support SLA Notably, CoinGecko’s free plan is one of the best among crypto APIs in terms of data offered for $0, but if you need heavy usage or guaranteed uptime, the cost can ramp up — at the high end, large enterprise users might negotiate custom plans beyond the listed Pro tier.
  1. CryptoCompare — Full Market Data + More CryptoCompare is a long-standing crypto data provider that offers a rich set of market data and analytics. It not only provides price data but also aggregates news, social sentiment, and even some on-chain data, making it a comprehensive source for crypto market Key features of CryptoCompare’s API include:
Market Data & Exchange Coverage: CryptoCompare covers 5,700+ coins and 260,000+ trading pairs across a wide array of exchanges. It collects trade data from more than 170 exchanges (both centralized and some decentralized) to produce its aggregate indices (known as CCCAGG prices). The API provides real-time price quotes, order book snapshots, trade history, and OHLCV candlesticks at various intervals. For advanced users, CryptoCompare can supply tick-level trade data and order book data for deep analysis (these are available via their WebSocket or extended API endpoints). Historical Data: CryptoCompare is strong in historical coverage. It offers historical daily data for many coins and historical intraday (minute) data as well. By default, all subscription plans include at least 7 days of minute-level history and full daily history; enterprise clients can get up to 1 year of minute-by-minute historical data (and raw trade data) for backtesting. This is valuable for quantitative researchers who require detailed price series. On-Chain Metrics and Other Data: In addition to market prices, CryptoCompare has expanded into on-chain metrics and alternative data. The API can provide certain blockchain statistics (they mention “blockchain metrics” and address data in their offerings)— for example, network transaction counts or wallet addresses for major chains. While it’s not as extensive as a dedicated on-chain provider, this allows blending on-chain indicators (like transaction volumes) with price data for analysis. CryptoCompare also integrates news feeds and social sentiment: the API has endpoints for the latest news articles and community sentiment analysis, which can help gauge market Reliability and Performance: CryptoCompare’s infrastructure is built for high performance. They claim support for up to 40,000 API calls per second bursts and hundreds of trades per second This makes it suitable for real-time applications and dashboards that need frequent updates. Their data is normalized through a proprietary algorithm to filter out bad data (e.g., outlier prices or exchange anomalies), aiming to deliver clean and consistent price indices (CCCAGG). The API itself is well-documented, and client libraries exist for languages like Python. Pricing & Limits: CryptoCompare historically offered a free public API (with IP-based limiting), but now uses an API key model with tiered plans. Personal/free use is still allowed — you can register for a free API key for non-commercial projects and get a decent allowance (exact call limits aren’t explicitly published, but users report free tiers on the order of a few thousand calls per day). For commercial or heavy use, their plans start around $80/month for a basic package and go up to ~$200/month for advanced packages. These plans might offer on the order of 100k to a few hundred thousand calls per month, plus higher data resolution. All plans grant access to ~60+ endpoints and features like full historical data download for daily/hourly (minute data beyond 7 days is enterprise-only). Enterprise solutions are available for customers needing custom data feeds, unlimited usage, white-label solutions, or bespoke datasets (pricing for these is via negotiation). In summary, CryptoCompare provides a very rich dataset and is priced in a mid-range: not as cheap as community resources, but more affordable than some institutional-grade providers. Its value is especially high if you need a mix of price, news, and basic on-chain data in one
  1. Glassnode — On-Chain Analytics Leader Glassnode is the premier platform for on-chain metrics and blockchain analytics. Unlike the other APIs in this list, Glassnode’s focus is less on real-time market prices and more on the fundamental health and usage of blockchain networks. It provides a wealth of on-chain data that is invaluable for crypto analysts and long-term investors. Key aspects of Glassnode’s API:
Extensive On-Chain Metrics: Glassnode offers over 800 on-chain metrics spanning multiple major blockchains (Bitcoin, Ethereum, Litecoin, and many others, as well as key ERC-20 tokens). This includes metrics like active addresses, transaction counts, transaction volumes, mining hash rates, exchange inflows/outflows, UTXO distributions, HODLer stats, realized cap, SOPR and much more. If you need to peer ino what’s happening inside a blockchain (not just its price on exchanges), Glassnode is the go-to source. For example, one can query the number of active Bitcoin addresses, the amount of BTC held by long-term holders vs. short-term, or Ethereum gas usage trends Market & Derivatives Data: In addition to pure on-chain data, Glassnode also incorporates off-chain market data for context. They provide spot price data for major assets (often used in tandem with metrics in their charts), and even some derivatives metrics (futures open interest, funding rates, etc. for major exchanges) at higher . This means Glassnode can be a one-stop shop for an analyst who wants to correlate on-chain activity with price movements or derivative market trends. Data Resolutions and API Access: The API allows retrieval of metrics at various time resolutions. Free users can typically access metrics at a daily resolution (one data point per day) and usually with a delayed timeframe (e.g. yesterday’s data). Paid tiers unlock higher frequency data — the mid-tier (Advanced) gives up to hourly data, and the top tier (Professional) can go down to 10-minute intervals for certain metrics This granularity is useful for near-real-time monitoring of on-chain events. It’s important to note that Glassnode’s API is primarily used for pulling time-series data of specific metrics (e.g., get the 24h moving average of active addresses, daily, over the last 5 years). The API is well-documented with a metric catalog detailing every metric and its available history and access tier. Analyst Tools: Glassnode provides an entire platform (Glassnode Studio) for visualizing these metrics with charts and alerts. While that’s beyond the API itself, it’s worth noting that many analysts use the web interface for research and the API for programmatic access when building models. Glassnode has become an industry standard for on-chain analysis — many research reports and crypto funds cite Glassnode metrics for insights on network adoption, investor behavior, and market cycles. Pricing & Limits: Glassnode’s offerings are tiered more by data access level than raw call counts. They have a Standard (Free) tier, an Advanced (Tier 2) paid tier, and a Professional (Tier 3) tier. The Free tier allows access to Basic metrics (Tier 1 metrics) at daily resolution, which covers a lot of fundamental data for major chains but not the more complex or derived metrics. The Advanced plan (around $29–$49 per month depending on promotions) unlocks Essential metrics (Tier 2) and provides up to hourly . The Professional plan (around $79 per month for individuals) gives access to all metrics (including Premium Tier 3 metrics) and finer resolution (10-min updates). However, there’s a catch: API access is only officially included for Professional/Enterprise users and may require a special add-on or enterprise . In practice, Glassnode does offer a free API but it is limited (e.g., you can query basic metrics via REST with a free API key, but many endpoints will return only if you have the right subscription). Enterprise clients who need programmatic access to extensive history or want to ingest Glassnode data into trading models can arrange custom packages (cost can run into the hundreds or thousands of dollars monthly for institutional licenses, which may include SLAs, custom metrics, or priority support). For the purpose of our comparison, Glassnode’s free option is great for community analysts to explore a subset of data, but serious use of their API requires the paid tiers. Glassnode is best suited for analysts and institutional users who heavily value on-chain rather than developers who just need straightforward price feeds. The table below summarizes the data coverage and features of these five API providers side-by-side: Ready to build with crypto data that just works? If you want reliable crypto prices + multi-asset coverage (stocks, FX, ETFs) + generous limits without piecing together 3–4 vendors, EODHD is the pragmatic pick. Why EODHD wins for most teams All-in-one: crypto + equities + FX under one API (consistent JSON/CSV). Great value: up to 100k calls/day from ~$19.99/mo — perfect for MVPs and production apps. Fast start: clean docs, code samples, Excel/Sheets add-ins, and bulk endpoints. Scale-ready: real-time REST & WebSocket, historical OHLCV, fundamentals, news. What you can ship this week Real-time crypto dashboards and alerts Backtests using years of OHLCV data Cross-asset analytics (BTC vs. S&P 500, ETH vs. USD) Spreadsheet models that refresh automatically 👉 Start for free with EODHD — grab your API key and make your first request in minutes.Try EODHD now (free tier available) and upgrade when you need more throughput. Top 5 Cryptocurrency Data APIs: Comprehensive Comparison (2025) was originally published in Coinmonks on Medium, where people are continuing the conversation by highlighting and responding to this story
Share
Medium2025/09/26 21:29
XRP Price Outlook As Peter Brandt Predicts BTC Price Might Crash to $42k

XRP Price Outlook As Peter Brandt Predicts BTC Price Might Crash to $42k

The post XRP Price Outlook As Peter Brandt Predicts BTC Price Might Crash to $42k appeared on BitcoinEthereumNews.com. XRP price led cryptocurrency losses on Friday
Share
BitcoinEthereumNews2026/02/06 19:06