API Documentation (player written)

Neptune’s Pride has an API that can be used to access scanning data. The API is pretty simple, but entirely undocumented. This post is an attempt to document it. I will edit the main post with details as they emerge, so if I have anything wrong, missed anything out or you have any questions let me know and I’ll work it in. However, this post will assume you have a basic understanding of APIs in general and how to work with them, so a degree of technical knowledge is assumed. The FAQ at the bottom also contain some more general information on what the API can and can’t do if you are worried about sharing your API key with someone.

Code examples in this use Python 3 and the requests library, as this is what I use. If you have code examples in different languages I’ll be happy to add them (except Python 2 because if you still use that you are bad and you should feel bad).

Many thanks particularly to AnnanFay in Discord for as a lot of what I used to get started are the various things they have pinned or posted there.

What does the API do?

The API can be used to access all of the scanning data a player has in any given game, as well as some core public information. It generates only a snapshot in time of the current state of the game when the API is called.

Certain information is available in game but NOT available in the API. Particularly, the API does not give access of any kind to messages, events, or the intelligence pages. Likewise, the API is read-only – you cannot issue orders or send any form of instructions to the game, only access the information.

The main way to use the API is to access your information for analysis or calculation in a more structured form.

API Parameters

The parameters you’ll need are the Game ID and the API Key.

The game ID is the long number at the end of your game URL:
game_id

To get the API Key, got to the options section of the game you want to connect to, scroll to the bottom, and hit “Generate key”. The key will be a short 6 character mixed upper and lower case code.
api_code

Note that you can only have one active API key. If you generate a new key, and old keys will be deactivated. This is particularly important if you are using another program that uses the key like NP2Stats – make sure you use the same key or you’ll be constantly regenerating between the two connections.

How to Call the API

To call the API you need to issue a POST request to the API end point with three parameters:

api_version : “0.1”
game_number: see above
code : see above

This will return a JSON file that contains all of your scanning data

Code Example

root = "https://np.ironhelmet.com/api"
params = {"game_number" : game_id,
                 "code" : api_key,
          "api_version" : "0.1"}
payload = requests.post(root, params).json()

Troubleshooting API Errors
When the API has been called correctly, you will get a payload with all your scanning data (see next section). However, if there is an error in the call it returns a simple JSON:

error : [Error Message]

The error messages you may received include the following options:

Error message Trigger
api_version not supported api_version is anything but 0.1*
unknown error while retrieving the game game_number is not a valid game number**
game not found game_number is a valid game number** but doesn’t correspond to a game
code not found in game code doesn’t match any API key generated for this game

*The “bad API” message will also come up in many other cases. Examples include using http instead of https or not submitted a form-encoded request. Thre may be others.
**Valid game numbers are integers between -2^63 and 2^63 - 1 inclusive but excluding 0.

Explaining the Payload

The payload will return a single JSON object, which contains a series of embedded JSON objects (in Python: dictionaries). These objects each contain a mixture of parameters and further embedded objects. The top level is a single key “scanning_data” and then within that there are several different object groups.

Basic Structure

Below is the basic structure that shows roughly the different sub-objects ( using {} ) and the types of key (using [ ]) present in the object.

scanning_data : { 
     [Game Details]
     fleets : {[Carrier Details]}
     stars : {[Star Details]}
     players : {[Player Details],
          tech : { {level, research progress*},
          war* : {}
          countdown_to_war* : {}
}

the * indicates this part will only be present for the player who owns the key.

The tables below describe the data contained within each JSON object. As objects are semi-structred, keys may not always be present. The Presence column indicates when a key may or may not be present.

Key Description
all key will always be present
owner Present if the entry belongs to the player who owns the API key
visible Present if the entry is visible to the player (stars only)
star Present if the entry is at a star (fleets only)
triton Only appears in Triton (can combine with other key tags)
proteus Only appears in Proteus (can combine with other key tags)

Game Details Keys

Field Presence Type Description
admin all int If the player who owns the key is admin. -1 = public game
fleet_price proteus int Incremental price for additional carriers (25 in public games)
fleet_speed all float Normal speed of carriers (1/24 of a distance unit)
fleets all object Object containing all fleet data
game_over all int If the game is completed. 0 = live, 1 = completed
name all str Name of the Game
now all int Time stamp for current time
paused all boolean If the game is paused. False = no, True = yes
player_uid all int The Player ID for the player who owns the key
players all object Object containing all player data
production_counter all int Current tick within this production cycle
production_rate all int Number of ticks per production cycle
productions all int Number of productions that have occurred so far
stars all object Object containing all stars data
stars_for_victory all int Number of stars required to win the game
start_time all int Time stamp for when the game started
started all boolean Whether the game has started. True = yes, False = No
tick all int Current tick, counted from the start of the game
tick_fragment all float Percentage of current tick that has been completed
tick_rate all int Number of minutes per tick
total_stars all int Total number of stars in the game
trade_cost all int Cost per level to trade technologies
trade_scanned all int Flag if trading is restricted to scanned players. 0 = no, 1 = yes
turn_based all int Flag if the game is turn based. 0 = no, 1 = yes
turn_based_time_out all int Deadline for turn submission, in a turn based game
war all int Unknown

Fleets Keys

Field Presence Type Description
exp proteus int Fleet experience level
l all int Looping, 1 = looped, 0 = not looped
lx all str X coordinate for the fleet’s location at the previous tick
ly all str y coordinate for the fleet’s location at the previous tick
n all str Name of the Carrier
o all list List of orders. See below
ouid star int Unique ID for the carrier’s current star
puid all int Player ID of the owner
sp proteus float Speed the fleet is moving at (in decimal)
st all int Number of ships (strength)
uid all int Unique ID for the carrier. Matches the object ID
w triton int Flag for the fleet is travelling at warp. 0 = no, 1 = yes
x all str X coordinate for fleet’s current location
y all str Y coordinate for the fleet’s current location

Carrier orders are presented as a list of lists. Each order is an ordered a list of 4 parameters with no key. The meaning by position is as follows:

Position Description
0 Delay value in ticks for the order
1 Unique ID for the star in the order
2 Order type ID (see below)
3 Number of ships in the order (e.g. Drop 20)

The order type is expressed as an integer with the following lookup:

Value Order
0 Do Nothing
1 Collect All
2 Drop All
3 Collect
4 Drop
5 Collect All But
6 Drop All But
7 Garrison Star

Stars Keys

Field Presence Type Description
c triton owned float Where ships/tick is not a whole number, the amount currently produced
e visible int Current level of economy
exp proteus owned int Unclear
ga visible int The presence of a warpgate. 0= no gate, 1 = gate
i visible int Current level of industry
n all str The current name of the star
nr visible int Natural resources of the star
puid all int Player ID of the player who owns the star
r triton visible int Resource level of the star (including terraforming bonus)
s visible int Current level of science
st visible int Number of ships on the star
uid all int Unique ID for the star (matches to the key in the parent object)
v all str Flag for if the star is visible. 0 = no, 1 = yes
x all str X coordinate of the star
y all str Y coordinate of the star

Players Keys

Field Presence Type Description
ai all int If the player is currently AI. 0 = no, 1 = yes
alias all str The Player’s alias / display name (not true alias)
avatar all int Unique ID for the player avatar
cash owner int Current funds available to the player
color proteus int Current player colour (can change in Proteus)
conceded all int If the player has exited the game. 0 = no, 1 = conceded, 2 = inactive, 3 = total wipe out
countdown_to_war owner object An object containing all player IDs and the number of ticks until war starts, if a permanent alliance has ended
fleet_price proteus int Cost to purchase the next carrier
huid all int Unique ID for the player’s home star
karma_to_give all int The amount of renown the player has not yet given in the game
ledger proteus object Dictionary of player ID (string) to ledger balance (int)
missed_turns all int Number of turns the player has missed
race proteus object 2 element list representing technology strength/weakness
ready all int If the player’s current turn is ready
regard all int The AI’s opinion of the player. Note that this may be present for non-AI players.
researching owner str The technology currently being researched
researching_next owner str The technology being researched next
stars_abandoned owner int Number of stars abandoned this production round (note: can’t be higher than 1, resets to 0 at prod)
ses proteus owner int Unknown
shape proteus int Current player shape (can change in Proteus)
tech all object Object containing player’s technology information
total_economy all int Total economy the player has
total_fleets all int Total number of carriers the player has
total_industry all int Total industry the player has
total_science all int Total science the player has
total_stars all int Total stars the player has
total_strength all int Total ships the player has
uid all int Unique ID for the player. Matches the object key
war owner dict Object containing the war status for every other player

Tech Keys
The main object contains 7 keys: one for each technology, which holds a further JSON object that contains the information about that technology for the player. Note that not all of the keys match exactly to the technology names we see in the game.

Field Presence Type Description
banking all str Banking
manufacturing all str Manufacturing
propulsion all str Hyperspace Range
research all str Experimentation
scanning all str Scanning
terraforming triton str Terraforming
weapons all str Weapons

Each technology has the following fields:

Field Presence Type Description
brr owner int Research cost per tech level for this technology (nb. calculation and values are different in triton/proteus)
bv owner int Used in value calculation, see below
level all int Current technology level
research owner int Current level of research progress towards this technology
sv owner int Used in value calculation, see below
value all int Current effect of the technology, see below

The Value field for tech levels is calculated from the ‘sv’ (start value) and ‘bv’ (base value, or the value per level). ‘value’ = ‘sv + level*bv’. For many techs, this just computes the level again. For example weapon strength is the level and weapons value is the weapon strength. For scanning and range (aka propulsion) the values are multiples of 1ly = 0.125, and can be used to determine range or scanning directly against the coordinate system of the map. It is unclear why the sv and bv of experimentation are what they are. No player has been able to map them onto anything meaningful in game.

War Keys
This object contains the player ID and a numeric key representing the formal alliance/war status for every other player in the game. In any state other than “0” the players are at war. All states can be distinguished on the other player’s empire page according to the prompt there, which offers you the ability to initiate an alliance (in state “3”, moves you to “2”), cancel that request (moves you from “2” to “3”), accept an alliance (in state 1, moves you to “0”), or declare war (in state “0”, starts the countdown to war with that player at 24 ticks which will appear in the “countdown_to_war” object. While not verified, it is believed you will be in state 0 until the countdown ends.

Value Meaning
0 Formal Alliance
1 Alliance Offered (no cost to accept)
2 Alliance Requested (paid $150)
3 At war

Countdown to War Keys
This object contains a key for each player ID with a single integer value indicated the number of ticks until war begins between the owning player and the player ID. In a game without Permanent Alliance enabled, it is always 3. Unknown for permanent alliance games.

Understanding the Coordinate System
NP uses a pretty standard X/Y coordinate system to pinpoint locations, including stars, fleets, fleet orders etc. All of them use the same X/Y system. However, translating this system to what we see on the map is not that straightforward as the map shows light years and they aren’t the same.

A value of 1 in the X/Y system is equal to 8 light years (e.g. 0,0 and 1,0 are 8 light years apart). Ships in the game move at 1 LY/tick at warp speed and 0.33 LY/tick at normal speed.

Understanding Time Fields
There are two types of time field in the NP API: ticks and timestamp. Ticks are relatively obvious, and correspond directly to the ticks we see in the game. The timestamp is used in a few different places, particularly to indicate the start and current time in the game settings data. The field gives a value in milliseconds of the time since 1/1/1970.

FAQ

My connection was working but now it isn’t, why not?

If you generated a new key, any previous keys will be deactivated. This is the most likely reason.

Can I use the API to issue orders?

No

Can I read events/messages with the API?

No

What happens if I give someone my API code?

They can access all of your scanning data in any way they see fit. They cannot issue orders, or read events or messages. If you want to revoke their access, generate a new API code and the code you gave them will no longer work.

What information can you get from the API but not from the game?
There are a few pieces of information that the API provides that are not readily available in the game’s normal interace:

  1. The API shows you the absolute X/Y coordinates of your stars. In most cases this is completely useless information unless you want to map the stars in an external program, but in a dark galaxy game it can give you a rough indication of which quadrant of the map you are in.

  2. You can see the amount of renown points each player has still left to give. Again, not terribly useful information, but it is there.

  3. You can see the precise home star of any given player (the game zooms roughly to this location when you click on the player but is not 100% precise)

Everything else is visible by other means in the game.

7 Likes

Documentation updated with the following:

Added explanations of Timestamp and Time
Added explanation of the X/Y coordinate system and how it relates to lightyears
Updated descriptions for “Fleet” and Carrier Orders
Updated Research Value calculation and related descriptions

With thanks to Matti on Discord for the relevant information.

Hey - great information - thanks for sharing :slight_smile: I’ve created a gist with a sample response from the server after the POST. Can be accessed here: Neptune's Pride Sample API Data · GitHub

1 Like

Documentation updated:

All sections updated to contain information from the API when run on a Proteus game.
Visibility amended for several fields to indicate when only present on Triton/Proteus
Player “huid” field description corrected (with thanks to Eutro on Discord for pointing out the mistake)

1 Like

You have an error in the “order” table: order nr 5 is “Collect all but”. “Drop all but” is nr 6.

“fleet_speed” is literally just that. If one distance unit is 8 ly and carriers fly 1/3 ly/h then it means they fly 24 distance units/h which is the float value stored in this field.

When the request is incorrect API returns a simple JSON:

error : [Error Message]

I managed to find the following error messages:

Error message Trigger
api_version not supported api_version is anything but 0.1*
unknown error while retrieving the game game_number is not a valid game number**
game not found game_number is a valid game number** but doesn’t correspond to a game
code not found in game code doesn’t match any API key generated for this game

*The “bad API” message will also come up in many other cases I didn’t bother documenting, like using http instead of https
**Valid game numbers are integers between -2^63 and 2^63 - 1 inclusive but excluding 0.

Additionally because JS converts strings to everything it doesn’t matter if api_key and game_number are strings or their respective types.

For turn based games, this is the epoch time, in milliseconds, at which the current turn ends/next turn will start.

Anyone know what the current valid api_version is? I’ve tried a number of different values.

POST https://np.ironhelmet.com/api
Content-Type: application/json

{
    "game_number": "<redacted>",
    "code": "<redacted>",
    "api_version": "0.1"
}
{
	"error": "api_version not supported"
}

It is still 0.1 ( I just checked) - this particular error message comes up fairly often for random causes, so it might be something else.

1 Like

In case anyone is curious, the request must be form-encoded:

POST https://np.ironhelmet.com/api
Content-Type: application/x-www-form-urlencoded

game_number=<redacted>&code=<redacted>&api_version=0.1
1 Like

Documentation updated:

  1. Details on failed requests included in the top
  2. Carrier order list corrected
  3. Fleet Speed and Turn Timeout descriptions added to the game information

Thanks to @olus2000 , @pouncywouncy and @jonah for providing the information for the update

1 Like

How do I make sence of the cooordinates that I recieve from API?
Where is the origin, and by what do I need to multiply them to aquire adequate results?

Okay, from what I gathered the field is reffered as 10*10 grid, but the origin of it is the coordinates of the home world of the one who API belongs to. Or the center of their starting position… I guess the same point that is shown when you ask NP to show you the player.

The origin is somewhere near the center of the map, they aren’t relative. 1 coordinate unit is equal to 8 in game ly so if you multiply them by 8 it should make a bit more sense to you. The origin is almost never an actual star though, it’s always right in the center of the galaxy

Erm… No. Rn I have origin on ~7x5, if I continue with my 10x10 grid.

Now that is usefull. Thank you!

Yes sorry you’re right, its not exactly center. I took a look through the map generation code again (The dev sent it to me after I emailed them) there is a random 2 unit shift on where the origin is. The random offset will shift both the x and y by a random value between -2 and 2. Since the code doesn’t always add the offset (there is an if statement) not all games will us the offset but whatever game you’re looking at obviously does

Here is the center_galaxy code in case you’re curious

def center_galaxy(universe, with_offset=True):
    # center the map in the universe
    min_x = 1000
    min_y = 1000
    max_x = -1000
    max_y = -1000
    for s in universe.stars.values():
        if s.x < min_x:
            min_x = s.x
        if s.y < min_y:
            min_y = s.y
        if s.x > max_x:
            max_x = s.x
        if s.y > max_y:
            max_y = s.y

    mid_x = (max_x + min_x) / 2
    mid_y = (max_y + min_y) / 2

    if with_offset:
        mid_x += random.randint(-2, 2)
        mid_y += random.randint(-2, 2)

    dif_x = 0 - mid_x
    dif_y = 0 - mid_y

    for s in universe.stars.values():
        s.x += dif_x
        s.y += dif_y
    for f in universe.fleets.values():
        f.x += dif_x
        f.y += dif_y
1 Like

I think the Y axis is flipped as well, with negative numbers appearing in the north. Can anyone confirm or am I talking crazy?

I’ll update the “Understanding the Coordinate System” section when confirmed

Yes the y axis is flipped and the x axis isn’t flipped. I’m not entirely sure why looking at the code I sent, so it seems like it’s just a weird quirk with coordinate system.

That is standard direction in programming, your monitor also has 0x0 in top left corner.

It’s not that all of the y axis is negative, its that y is flipped along the center of the grid. Negative numbers are in the north and positive numbers are in the north