├── .travis.yml ├── API-Introduction-Part-1-Setup.md ├── API-Introduction-Part-2-Account-Concepts.md ├── API-Introduction-Part-3-Manifest.md ├── API-Introduction-Part-4-Item-Concepts.md ├── API-Introduction-Part-5-Item-Examples.md ├── API-Introduction-Part-X-Other-Resources.md ├── Clan-Banner-Images.md ├── Destiny-2-Open-Source-Projects.md ├── Home.md ├── Leviathan-Raid-Encounter-Rotation-Info.md ├── Locations-of-Specific-Game-Information-in-the-API.md ├── Manifest-Queries-with-JSON1-Extension.md └── _Footer.md /.travis.yml: -------------------------------------------------------------------------------- 1 | script: 2 | - |- 3 | git config user.name "vpzed" 4 | git config user.email "30138020+vpzed@users.noreply.github.com" 5 | git remote remove origin 6 | git remote add origin https://$GITHUB_API_KEY@github.com/vpzed/Destiny2-API-Info-wiki.git > /dev/null 2>&1 7 | git remote add upstream https://$GITHUB_API_KEY@github.com/vpzed/Destiny2-API-Info.wiki.git > /dev/null 2>&1 8 | git fetch origin 9 | git fetch upstream 10 | git merge upstream/master --no-edit 11 | git push origin HEAD:master > /dev/null 2>&1 12 | git push upstream HEAD:master > /dev/null 2>&1 13 | env: 14 | matrix: 15 | secure: u8DQ+n2XEPPDik6C2S6UxXm5H+161hTGebtXZN0KPf9aAgGU0PBAjIxzIGC21PTvKsfBbjQifuXMtHrCxYvJ4r2RVLhL5cLBaG7b8vx5O+eJZ/R323YQzleshlaTLPzltzYPW+rmzhdadtr4a/HJL1TbApKDYhlstgidL536BqwTakql3BfFaZzuYd4f2GUi4860Y2u83CEg+DFWwzc8FWu9jr5MeE8FNBiTQ69jfkWIQYPKv1jqYTC9BdLpZYiVWliatHMlyUL9w8hCrROv4znqMLpHqomSZKD/cGlJD2Cq7aW3dvbbRgSl5s4Ahveerjhm5GU0eT61bU0LtbbylZi4PXfXFHqpmZB9pzm21c0EILawsUFCtXItHb+DMs3wpIzmTFqKMLLqPHtEiYAx5REA6DelsWwYVNSbkRyEmTDi3nOJ7oetVxQk048892TehjHf8TfwthTBjdSbajvV9+PQkQghuoMvsxwyZi3+PhbwFOAcjaqMQ36ozEr8KVMtMOaIZ8u0MNDd/y5ZE/VIshcM+u3om/iTqLCNZokLwxBarUB09/9zPStMQptwYK+Ctl5uN+tY7FRj/Ov8yasrSTIkCxvfu1+o3vYed1M9y4m3WEpIb1Ne85OaGtPRklGp6UMkfICtBQ9XanTWlLbyzm350EjNg30a8waz83TFPfo= 16 | -------------------------------------------------------------------------------- /API-Introduction-Part-1-Setup.md: -------------------------------------------------------------------------------- 1 | # Destiny 2 API Introduction - Part 1 - Setup 2 | 3 | ## Introduction 4 | 5 | This tutorial series will go over some beginner information on how to use the Destiny 2 API. This section will go over the initial setup needed to use the API for testing public API endpoint requests and responses. 6 | 7 | 8 | ## Bungie.net API Key 9 | 10 | This article assumes that you already have a Bungie.net account. If you do not already have one then you can create one here https://www.bungie.net/en/User/JoinUp 11 | 12 | When logged into your Bungie.net account you can access the Application page to request an API key here https://www.bungie.net/en/Application 13 | 14 | Select the Create New App button to enter the New Application screen. You can create multiple applications with different configurations. For this initial introduction we will not be using OAuth authentication. This topic will be covered in a future article. 15 | 16 | In the New Application screen enter your Application Name and Website. In the App Authentication > OAuth Client Type, select Not applicable. Leave the Redirect URL blank. Under Scope select, "Read your Destiny vault and character inventory" and "Read your Destiny vendor and advisor information". Under Browser Based Apps > Origin Header enter * Check the box to agree to the Terms of Use, and click the Create New App button. 17 | 18 | Now in the API Keys section you should see your API key. You will use this key in every Bungie API request you make. This key is private and should not be shared, published in your source code, etc. Copy the API Key into a safe place as you will use it later in this tutorial. 19 | 20 | For a detailed look at the concepts on the Bungie.net Application page [please see this article](https://github.com/xlxCLUxlx/Destiny2API/wiki/Bungie.net-Application-Portal) 21 | 22 | 23 | ## Destiny 2 API Documentation 24 | 25 | The home of Destiny 2 API information is https://github.com/Bungie-net/api and the main API documentation like is https://bungie-net.github.io/multi/index.html so bookmark these links as you will refer to them frequently. It will be helpful to reference the main API documentation while following along with this tutorial. 26 | 27 | It is recommended that you create a GitHub account as this allows you to post or follow Issues for the repository. 28 | 29 | 30 | ## API Tool 31 | 32 | To perform your initial interactions with the API the use of an API tool is highly recommended. The Postman tool is free and is what we'll use in this tutorial. Postman can be downloaded from https://www.getpostman.com/ for macOS, Windows, and Linux. 33 | 34 | Once Postman is installed, use the Github account you created to sign-in to Postman. This will save your setup and sync it across installations. 35 | 36 | In Postman click Collection > New Collection and name it Destiny or something you want to use for your project. A Collection is a group of API request setups. You can then save different API endpoints so you don't have to re-enter the information each time. 37 | 38 | In the middle panel you should see an API request tab. There is a URL input box next to the request type which defaults to GET. Just under that box there is a row of options: Authorization, Headers, Body, Pre-request Script, Tests. Select the Headers option. 39 | 40 | On the right-hand side of the window in the Headers screen click Presets > Manage Presets. In the pop-up window click Add. Enter a preset name and then in the boxes below enter: 41 | 42 | Key: X-API-KEY 43 | Value: {paste your API key here} 44 | Description: Destiny 45 | 46 | Save and close the Manage Presets screen. You should now see your preset in the Preset menu. Select your preset to add your API key to the request. It should show up in the Headers with a checkbox on the left. You will need to use your preset to add the API key header to each request you build in Postman. 47 | 48 | 49 | ## First API Request 50 | 51 | Now on to your first API request! We'll use the popular Destiny streamer Datto to do an example request. We'll explain more about API requests in the next section of the tutorial, but for now enter this in the box next to GET: 52 | 53 | https://www.bungie.net/Platform/Destiny2/SearchDestinyPlayer/-1/dattowatto/ 54 | 55 | Click the Send button. If you entered your API key correctly you should see this response (or similar): 56 | 57 | { 58 | "Response": [ 59 | { 60 | "iconPath": "/img/theme/destiny/icons/icon_psn.png", 61 | "membershipType": 2, 62 | "membershipId": "4611686018428389623", 63 | "displayName": "Dattowatto" 64 | } 65 | ], 66 | "ErrorCode": 1, 67 | "ThrottleSeconds": 0, 68 | "ErrorStatus": "Success", 69 | "Message": "Ok", 70 | "MessageData": {} 71 | } 72 | 73 | Click the Save button, enter Destiny2.SearchDestinyPlayer, select your Destiny collection, and click Save to save your new request to the Collection. 74 | 75 | This completes Part 1 - Setup. In Part 2 we'll get into some fundamental concepts for using the API. 76 | 77 | 78 | -------------------------------------------------------------------------------- /API-Introduction-Part-2-Account-Concepts.md: -------------------------------------------------------------------------------- 1 | # Destiny 2 API Introduction - Part 2 - Account Concepts 2 | 3 | ## Introduction 4 | 5 | In Part 1 - Setup we got our API key and setup Postman to start using the Destiny 2 API. In this tutorial we'll continue with that foundation. 6 | 7 | Each request we build will follow the same concept as Part 1. We'll open a new request tab in Postman, use our Preset to add the API key header, and enter the API endpoint URL in the GET box. Once we test that it is working, we'll save that request to the Collection. As you work, you'll build out a Collection of all the requests you use regularly so you can see how they work and have example responses that you can use to build your application/website code around. 8 | 9 | 10 | ## Accounts 101 11 | 12 | The Destiny 2 API uses many different kinds of accounts, account types, and groups. Here I'll briefly summarize the most common pieces. 13 | 14 | The first concept is membershipType and membershipId. An account is represented by a pair of integers - the membershipType (int32) and membershipId (int64). A Bungie account is always membershipType 254. A Destiny account has a different membershipType depending on the associated game platform with Xbox Live: 1, PlayStation Network: 2 and Battle.net: 4. A few search related endpoints also allow the special account type of All: -1; however, most endpoints require a specific matching pair of membershipType and membershipID. 15 | 16 | NOTE: In Part 1 we used the special account type of -1 in our Destiny2.SearchDestinyPlayer request. If you only want to search a specific type of membership like say Battle.net then you can use say 4 instead of -1, which will then only return matches for that platform. 17 | 18 | So if we go back to the API Response from Part 1: 19 | 20 | { 21 | "Response": [ 22 | { 23 | "iconPath": "/img/theme/destiny/icons/icon_psn.png", 24 | "membershipType": 2, 25 | "membershipId": "4611686018428389623", 26 | "displayName": "Dattowatto" 27 | } 28 | ], 29 | "ErrorCode": 1, 30 | "ThrottleSeconds": 0, 31 | "ErrorStatus": "Success", 32 | "Message": "Ok", 33 | "MessageData": {} 34 | } 35 | 36 | We can see that for this account the membershipType is 2 so this is a Destiny account for PSN, and the gamertag is Dattowatto as expected. Also the unique identifier for this account is the pair of integers membershipType: 2 and membershipId: 4611686018428389623. 37 | 38 | Each Destiny account can have up to three characters which are associated with a characterId which is also an integer (int64). 39 | 40 | Accounts may also join Bungie.net Groups which are entities on the Bungie.net website, and players may join Clans which are a special type of Group. Groups are identified by a groupId (int64) and a groupType (int32). Bungie.net Groups are groupType: 0, and Clans are groupType: 1. 41 | 42 | You will see many references throughout the documentation for membershipType, membershipId, characterId, groupType, and groupId. 43 | 44 | 45 | ## More API Requests 46 | 47 | It is common to use a gamertag as an API entrypoint. As before, we can use the gamertag to get the account information using the Destiny2.SearchDestinyPlayer endpoint. We'll continue with the Datto example. NOTE: Gamertags must be URL encoded, so say a Battle.Net user of test#1224 would become test%231234 48 | 49 | A very frequently used endpoint is the Destiny2.GetProfile. This endpoint uses the concept of components to return a wide array of information about a Destiny account. 50 | 51 | Let's use the Destiny account information we retrieved for Datto to get a list of the characterIds associated with that account: 52 | 53 | https://www.bungie.net/Platform/Destiny2/2/Profile/4611686018428389623/?components=100 54 | 55 | { 56 | "Response": { 57 | "profile": { 58 | "data": { 59 | "userInfo": { 60 | "membershipType": 2, 61 | "membershipId": "4611686018428389623", 62 | "displayName": "Dattowatto" 63 | }, 64 | "dateLastPlayed": "2017-12-15T23:55:21Z", 65 | "versionsOwned": 3, 66 | "characterIds": [ 67 | "2305843009262373961", 68 | "2305843009262373962", 69 | "2305843009286248696" 70 | ] 71 | }, 72 | "privacy": 1 73 | }, 74 | "itemComponents": {} 75 | }, 76 | "ErrorCode": 1, 77 | "ThrottleSeconds": 0, 78 | "ErrorStatus": "Success", 79 | "Message": "Ok", 80 | "MessageData": {} 81 | } 82 | 83 | So now we can see that Dattowatto has three characters on this account and we have the characterIds. Now we can use the Destiny2.GetCharacter endpoint to get additional information about a character. In this case we'll get the character summary. This endpoint also uses the concept of components so there are lots of different options available as described in the official documentation. 84 | 85 | https://www.bungie.net/Platform/Destiny2/2/Profile/4611686018428389623/Character/2305843009262373961/?components=200 86 | 87 | { 88 | "Response": { 89 | "character": { 90 | "data": { 91 | "membershipId": "4611686018428389623", 92 | "membershipType": 2, 93 | "characterId": "2305843009262373961", 94 | "dateLastPlayed": "2017-12-15T23:55:21Z", 95 | "minutesPlayedThisSession": "161", 96 | "minutesPlayedTotal": "13600", 97 | "light": 308, 98 | "stats": { 99 | "144602215": 0, 100 | "392767087": 4, 101 | "1735777505": 0, 102 | "1885944937": 303, 103 | "1935470627": 308, 104 | "1943323491": 9, 105 | "2715839340": 20, 106 | "2996146975": 2, 107 | "3555269338": 53, 108 | "3897883278": 0, 109 | "4188031367": 40, 110 | "4244567218": 0 111 | }, 112 | "raceHash": 898834093, 113 | "genderHash": 3111576190, 114 | "classHash": 3655393761, 115 | "raceType": 2, 116 | "classType": 0, 117 | "genderType": 0, 118 | "emblemPath": "/common/destiny2_content/icons/0b60b0d2d119a35a7883d81d9e409204.jpg", 119 | "emblemBackgroundPath": "/common/destiny2_content/icons/5650931ed6d9e901967e31bb9c804767.jpg", 120 | "emblemHash": 3115055261, 121 | "emblemColor": { 122 | "red": 2, 123 | "green": 3, 124 | "blue": 4, 125 | "alpha": 255 126 | }, 127 | "levelProgression": { 128 | "progressionHash": 1716568313, 129 | "dailyProgress": 0, 130 | "dailyLimit": 0, 131 | "weeklyProgress": 0, 132 | "weeklyLimit": 0, 133 | "currentProgress": 196000, 134 | "level": 25, 135 | "levelCap": 25, 136 | "stepIndex": 25, 137 | "progressToNextLevel": 0, 138 | "nextLevelAt": 10000 139 | }, 140 | "baseCharacterLevel": 25, 141 | "percentToNextLevel": 0 142 | }, 143 | "privacy": 1 144 | }, 145 | "itemComponents": {} 146 | }, 147 | "ErrorCode": 1, 148 | "ThrottleSeconds": 0, 149 | "ErrorStatus": "Success", 150 | "Message": "Ok", 151 | "MessageData": {} 152 | } 153 | 154 | As you can see lots of information here and a lot of it probably doesn't make a lot of sense yet. This is due to Bungie's desire to make the API responsive to localization. Therefore most of the data returned is a reference which must be "de-referenced" with a localized game manifest to allow it to be presented in the respective language. The use of the game manifest is covered in in the next article of this series. 155 | 156 | One quick note image links in the API response have a root of https://www.bungie.net so the emblemPath above would translate to https://www.bungie.net/common/destiny2_content/icons/0b60b0d2d119a35a7883d81d9e409204.jpg 157 | 158 | 159 | Let's say you wanted to know about which Clan Dattowatto was a member. To look this up you'd use the GroupV2.GetGroupsForMember endpoint. In this example we are using a filter: 0 (All) and a groupType: 1 for Clans. 160 | 161 | https://www.bungie.net/Platform/GroupV2/User/2/4611686018428389623/0/1/ 162 | 163 | 164 | { 165 | "Response": { 166 | "results": [ 167 | { 168 | "member": { 169 | "memberType": 3, 170 | "isOnline": false, 171 | "groupId": "881267", 172 | "destinyUserInfo": { 173 | "iconPath": "/img/theme/destiny/icons/icon_psn.png", 174 | "membershipType": 2, 175 | "membershipId": "4611686018428389623", 176 | "displayName": "Dattowatto" 177 | }, 178 | "joinDate": "2016-05-04T07:05:20Z" 179 | }, 180 | "group": { 181 | "groupId": "881267", 182 | "name": "Math Class", 183 | "groupType": 1, 184 | "membershipIdCreated": "3952103", 185 | "creationDate": "2015-03-09T03:11:14.742Z", 186 | "modificationDate": "2017-10-01T13:41:23.811Z", 187 | "about": "http://math.gg", 188 | "tags": [], 189 | "memberCount": 94, 190 | "isPublic": true, 191 | "isPublicTopicAdminOnly": false, 192 | "primaryAlliedGroupId": "0", 193 | "motto": "World Class Destiny Clan. English only, please.", 194 | "allowChat": true, 195 | "isDefaultPostPublic": false, 196 | "chatSecurity": 0, 197 | "locale": "en", 198 | "avatarImageIndex": 70025, 199 | "homepage": 1, 200 | "membershipOption": 2, 201 | "defaultPublicity": 2, 202 | "theme": "Group_Community1", 203 | "bannerPath": "/img/Themes/Group_Community1/struct_images/group_top_banner.jpg", 204 | "avatarPath": "/img/profile/avatars/group/025.png", 205 | "isAllianceOwner": false, 206 | "conversationId": "9537586", 207 | "enableInvitationMessagingForAdmins": false, 208 | "banExpireDate": "2001-01-01T00:00:00Z", 209 | "features": { 210 | "maximumMembers": 100, 211 | "maximumMembershipsOfGroupType": 1, 212 | "capabilities": 31, 213 | "membershipTypes": [ 214 | 1, 215 | 2, 216 | 4, 217 | 10 218 | ], 219 | "invitePermissionOverride": true, 220 | "updateCulturePermissionOverride": true, 221 | "hostGuidedGamePermissionOverride": 2, 222 | "updateBannerPermissionOverride": true, 223 | "joinLevel": 2 224 | }, 225 | "clanInfo": { 226 | "d2ClanProgressions": { 227 | "584850370": { 228 | "progressionHash": 584850370, 229 | "dailyProgress": 300000, 230 | "dailyLimit": 0, 231 | "weeklyProgress": 100000, 232 | "weeklyLimit": 100000, 233 | "currentProgress": 300000, 234 | "level": 3, 235 | "levelCap": 6, 236 | "stepIndex": 3, 237 | "progressToNextLevel": 75000, 238 | "nextLevelAt": 125000 239 | }, 240 | "1273404180": { 241 | "progressionHash": 1273404180, 242 | "dailyProgress": 0, 243 | "dailyLimit": 0, 244 | "weeklyProgress": 0, 245 | "weeklyLimit": 0, 246 | "currentProgress": 0, 247 | "level": 1, 248 | "levelCap": 6, 249 | "stepIndex": 1, 250 | "progressToNextLevel": 0, 251 | "nextLevelAt": 1 252 | }, 253 | "3381682691": { 254 | "progressionHash": 3381682691, 255 | "dailyProgress": 0, 256 | "dailyLimit": 0, 257 | "weeklyProgress": 0, 258 | "weeklyLimit": 0, 259 | "currentProgress": 0, 260 | "level": 1, 261 | "levelCap": 6, 262 | "stepIndex": 1, 263 | "progressToNextLevel": 0, 264 | "nextLevelAt": 1 265 | }, 266 | "3759191272": { 267 | "progressionHash": 3759191272, 268 | "dailyProgress": 0, 269 | "dailyLimit": 0, 270 | "weeklyProgress": 0, 271 | "weeklyLimit": 0, 272 | "currentProgress": 0, 273 | "level": 1, 274 | "levelCap": 6, 275 | "stepIndex": 1, 276 | "progressToNextLevel": 0, 277 | "nextLevelAt": 1 278 | } 279 | }, 280 | "clanCallsign": "MATH", 281 | "clanBannerData": { 282 | "decalId": 4142223390, 283 | "decalColorId": 3312277351, 284 | "decalBackgroundColorId": 3501638311, 285 | "gonfalonId": 1473910866, 286 | "gonfalonColorId": 2157636321, 287 | "gonfalonDetailId": 1664476157, 288 | "gonfalonDetailColorId": 4095345228 289 | } 290 | } 291 | } 292 | } 293 | ], 294 | "totalResults": 1, 295 | "hasMore": false, 296 | "query": { 297 | "itemsPerPage": 1000, 298 | "currentPage": 1 299 | }, 300 | "useTotalResults": true 301 | }, 302 | "ErrorCode": 1, 303 | "ThrottleSeconds": 0, 304 | "ErrorStatus": "Success", 305 | "Message": "Ok", 306 | "MessageData": {} 307 | } 308 | 309 | Again lots of information here, but the key information is yes Dattowatto is in a Clan with groupId: 881267, and name: "Math Class". 310 | 311 | Hopefully this helps introduce the concepts of accounts and groups in Destiny 2. In Part 3 we'll get started with some introductory information on the game manifest. 312 | 313 | -------------------------------------------------------------------------------- /API-Introduction-Part-3-Manifest.md: -------------------------------------------------------------------------------- 1 | # Destiny 2 API Introduction - Part 3 - Manifest 2 | 3 | ## Introduction 4 | 5 | The Destiny game manifest is a SQLite database that comes in several languages with localized versions of the game information specific to that language. Most of the API responses utilize references (often {something}Hash) to point the user to the manifest. 6 | 7 | 8 | ## Retrieving the Manifest Database 9 | 10 | The endpoint URL to retrieve the base information about the manifest is: 11 | 12 | https://www.bungie.net/Platform/Destiny2/Manifest/ 13 | 14 | { 15 | "Response": { 16 | "version": "61252.17.12.02.0200-8", 17 | "mobileAssetContentPath": "/common/destiny2_content/sqlite/asset/asset_sql_content_8bd670a833634c5c92de2c96ba514f3e.content", 18 | "mobileGearAssetDataBases": [ 19 | { 20 | "version": 0, 21 | "path": "/common/destiny2_content/sqlite/asset/asset_sql_content_8bd670a833634c5c92de2c96ba514f3e.content" 22 | }, 23 | { 24 | "version": 1, 25 | "path": "/common/destiny2_content/sqlite/asset/asset_sql_content_d604e05133733779297d1548f0ee208c.content" 26 | }, 27 | { 28 | "version": 2, 29 | "path": "/common/destiny2_content/sqlite/asset/asset_sql_content_d604e05133733779297d1548f0ee208c.content" 30 | } 31 | ], 32 | "mobileWorldContentPaths": { 33 | "en": "/common/destiny2_content/sqlite/en/world_sql_content_8ce5b3356e8749ddcb7f81c0ac8875c6.content", 34 | "fr": "/common/destiny2_content/sqlite/fr/world_sql_content_4100c5c985b2488c44a6ebee97037677.content", 35 | "es": "/common/destiny2_content/sqlite/es/world_sql_content_9bfbf7737c5982d9b0bea1ce98e8e898.content", 36 | "de": "/common/destiny2_content/sqlite/de/world_sql_content_2bacc28fa685ab4c2c49257a46742668.content", 37 | "it": "/common/destiny2_content/sqlite/it/world_sql_content_d8b5fc81eebec797ba66f1e0868361c7.content", 38 | "ja": "/common/destiny2_content/sqlite/ja/world_sql_content_ea68ca09f36431845bbafa8e273e8ad9.content", 39 | "pt-br": "/common/destiny2_content/sqlite/pt-br/world_sql_content_6bae2d1604335fa58328c45e935c5d25.content", 40 | "es-mx": "/common/destiny2_content/sqlite/es-mx/world_sql_content_85f7afcf28b78176f669d2d435afa18f.content", 41 | "ru": "/common/destiny2_content/sqlite/ru/world_sql_content_45cddf538cfe61d81cc8d54f800400ac.content", 42 | "pl": "/common/destiny2_content/sqlite/pl/world_sql_content_b11a2fd690037964075c3e51224485b9.content", 43 | "zh-cht": "/common/destiny2_content/sqlite/zh-cht/world_sql_content_33e179d09ec3c09c05792906feaec03c.content" 44 | }, 45 | "mobileClanBannerDatabasePath": "/common/destiny2_content/clanbanner/clanbanner_sql_content_13bcd2427f07d0121bb532a1a0638ba4.content", 46 | "mobileGearCDN": { 47 | "Geometry": "/common/destiny2_content/geometry/platform/mobile/geometry", 48 | "Texture": "/common/destiny2_content/geometry/platform/mobile/textures", 49 | "PlateRegion": "/common/destiny2_content/geometry/platform/mobile/plated_textures", 50 | "Gear": "/common/destiny2_content/geometry/gear", 51 | "Shader": "/common/destiny2_content/geometry/platform/mobile/shaders" 52 | } 53 | }, 54 | "ErrorCode": 1, 55 | "ThrottleSeconds": 0, 56 | "ErrorStatus": "Success", 57 | "Message": "Ok", 58 | "MessageData": {} 59 | } 60 | 61 | 62 | The main game world database is listed in the Response.mobileWorldContentPaths object for your desired language. I use English so the one I download is Response.mobileWorldContentPaths.en The other manifest databases like the mobileClanBannerDatabasePath are for very specific use cases and will not be covered in this tutorial. 63 | 64 | The base URL is https://www.bungie.net so for for the Response.mobileWorldContentPaths.en above that would give: 65 | 66 | https://www.bungie.net/common/destiny2_content/sqlite/en/world_sql_content_8ce5b3356e8749ddcb7f81c0ac8875c6.content 67 | 68 | Hitting that URL will download a file with the same name so in this case world_sql_content_8ce5b3356e8749ddcb7f81c0ac8875c6.content 69 | 70 | Even though it doesn't indicate it with a file extension that is a ZIP file, so you need to unzip it. Since the file inside the zip has the same name I rename it to world_sql_content_8ce5b3356e8749ddcb7f81c0ac8875c6.zip and then extract the archive. As mentioned the unzipped file will be world_sql_content_8ce5b3356e8749ddcb7f81c0ac8875c6.content again. This is the SQLite database file so I rename this to world_sql_content_8ce5b3356e8749ddcb7f81c0ac8875c6.sqlite3 for easy recognition on my PC. In your code these renaming steps are not required. 71 | 72 | NOTE: The string "8ce5b3356e8749ddcb7f81c0ac8875c6" is the md5sum of the **unzipped** manifest file, so you can use the file name to verify the checksum and know your file came through the process OK. 73 | 74 | Then you use a SQLite3 program to interact with that unzipped file. For this tutorial I will use [DB Browser for SQLite](http://sqlitebrowser.org/) and the [SQLite docs](https://sqlite.org/). 75 | 76 | All but one of the tables have an 'id' column and 'json' column. The 'json' column contains a JSON object with the actual data inside which is a lot like working with the JSON responses from the API. One exception is the DestinyHistoricalStatsDefinition table which uses a 'key' and 'json' setup. The table names use a format of Destiny{identifier}Definition so DestinyRaceDefinition is the table with Race information. 77 | 78 | 79 | ## Manifest Updates 80 | 81 | The manifest updates on an irregular schedule. If your application relies heavily on the manifest you may want to check for updates every day. A simple method for checking for a new manifest is to compare the URL of the desired manifest file (i.e. Response.mobileWorldContentPaths.en for English) between retrievals. If the name has changed then the manifest has updated. 82 | 83 | 84 | ## Manifest Lookups 85 | 86 | The most common use case for the manifest is the API is going to give you something like say raceHash = 2803282938 but what you want is the name of that race so you need to look that up in the manifest. 87 | 88 | First, you must convert the hash from a value that matches the ID column. For a `raceHash` of `2803282938` it will need to be converted to the ID `-1491684358`. See [Converting hashes for the SQLite DB](#converting-hashes-for-the-sqlite-db) for more details and code samples 89 | 90 | Then use that id in a SQL query like: 91 | 92 | SELECT json FROM DestinyRaceDefinition WHERE id = -1491684358; 93 | 94 | 95 | NOTE: For some additional code examples see the DestinyDevs Wiki [here](http://destinydevs.github.io/BungieNetPlatform/docs/Manifest). This is for the D1 manifest but the same concepts apply. 96 | 97 | Another way to query the manifest is the use the SQLite JSON extensions. This allows you to query the json column directly and bypass the use of the id column and the conversions it entails. 98 | 99 | To perform the same query with JSON extensions you would use a SQL query like: 100 | 101 | SELECT json_extract(DestinyRaceDefinition.json, '$') 102 | FROM DestinyRaceDefinition, json_tree(DestinyRaceDefinition.json, '$') 103 | WHERE json_tree.key = 'hash' AND json_tree.value = 2803282938 104 | 105 | This would return: 106 | 107 | { 108 | "displayProperties": { 109 | "description": "The Awoken survived the Collapse in deep space. But they were forever changed.", 110 | "name": "Awoken", 111 | "hasIcon": false 112 | }, 113 | "raceType": 1, 114 | "genderedRaceNames": { 115 | "Male": "Awoken Male", 116 | "Female": "Awoken Female" 117 | }, 118 | "hash": 2803282938, 119 | "index": 1, 120 | "redacted": false, 121 | "blacklisted": false 122 | } 123 | 124 | So you can see the displayProperties.name value is Awoken. 125 | 126 | A separate article on this Wiki includes many example queries for the use of JSON extensions. 127 | 128 | 129 | ## Another Example 130 | 131 | In Part 2 we looked-up the information for one of Dattowatto's characters and that returned "classHash": 3655393761. Some of the references are clear like this one in that classHash points us to the DestinyClassDefinition table. 132 | 133 | Let's say all we wanted was the class name. To accomplish that we'd use this SQL query: 134 | 135 | SELECT json_extract(DestinyClassDefinition.json, '$.displayProperties.name') 136 | FROM DestinyClassDefinition, json_tree(DestinyClassDefinition.json, '$') 137 | WHERE json_tree.key = 'hash' AND json_tree.value = 3655393761 138 | 139 | which returns Titan. 140 | 141 | or using the id field 3655393761 converts to -639573535 so we'd use: 142 | 143 | SELECT json FROM DestinyClassDefinition WHERE id = -639573535; 144 | 145 | which returns: 146 | 147 | { 148 | "classType": 0, 149 | "displayProperties": { 150 | "name": "Titan", 151 | "hasIcon": false 152 | }, 153 | "genderedClassNames": { 154 | "Male": "Titan", 155 | "Female": "Titan" 156 | }, 157 | "hash": 3655393761, 158 | "index": 0, 159 | "redacted": false, 160 | "blacklisted": false 161 | } 162 | 163 | ## Converting hashes for the SQLite DB 164 | 165 | In the API, hashes (like item hashes) are can be represented as an "unsigned 32 int". However, in the SQLiteDB, these hashes are represented as a "signed 32 int". The unsigned 32 int overflows what can be stored in SQLite's signed 32 int, so the majority of items in the DB will have negative ids. For example, the item "Vigil of Heroes" with item hash `2592351697` has an ID value of `-1702615599` in the definitions SQLite database. 166 | 167 | To query the SQLite DB for a hash, you will need to convert the hash from an "unsigned 32 int" to a "signed 32 int". 168 | 169 | In Python: 170 | 171 | ```python 172 | id = int(hash) 173 | if (id & (1 << (32 - 1))) != 0: 174 | id = id - (1 << 32) 175 | ``` 176 | 177 | In C#: 178 | 179 | ```csharp 180 | var id = unchecked((int) hash); 181 | ``` 182 | 183 | In Javascript: 184 | 185 | ``` 186 | const id = hash >> 32; 187 | ``` 188 | 189 | ## Manifest Tables 190 | 191 | As of Dec 2017 there are 39 manifest tables detailing game information. 192 | 193 | DestinyActivityDefinition 194 | DestinyActivityGraphDefinition 195 | DestinyActivityModeDefinition 196 | DestinyActivityModifierDefinition 197 | DestinyActivityTypeDefinition 198 | DestinyBondDefinition 199 | DestinyClassDefinition 200 | DestinyDamageTypeDefinition 201 | DestinyDestinationDefinition 202 | DestinyEnemyRaceDefinition 203 | DestinyEquipmentSlotDefinition 204 | DestinyFactionDefinition 205 | DestinyGenderDefinition 206 | DestinyHistoricalStatsDefinition 207 | DestinyInventoryBucketDefinition 208 | DestinyInventoryItemDefinition 209 | DestinyItemCategoryDefinition 210 | DestinyItemTierTypeDefinition 211 | DestinyLocationDefinition 212 | DestinyLoreDefinition 213 | DestinyMedalTierDefinition 214 | DestinyMilestoneDefinition 215 | DestinyObjectiveDefinition 216 | DestinyPlaceDefinition 217 | DestinyProgressionDefinition 218 | DestinyProgressionLevelRequirementDefinition 219 | DestinyRaceDefinition 220 | DestinyReportReasonCategoryDefinition 221 | DestinyRewardSourceDefinition 222 | DestinySackRewardItemListDefinition 223 | DestinySandboxPerkDefinition 224 | DestinySocketCategoryDefinition 225 | DestinySocketTypeDefinition 226 | DestinyStatDefinition 227 | DestinyStatGroupDefinition 228 | DestinyTalentGridDefinition 229 | DestinyUnlockDefinition 230 | DestinyVendorCategoryDefinition 231 | DestinyVendorDefinition 232 | 233 | 234 | ## Unofficial Manifest Resources 235 | 236 | There are a number of unofficial game manifest resources. Here are a couple that provide different ways to get manifest data: 237 | 238 | https://destiny.plumbing/ provides processed manifest data in JSON format for Destiny 2. 239 | 240 | https://destinysets.com/data provides a visual manifest interface for Destiny 2. 241 | -------------------------------------------------------------------------------- /API-Introduction-Part-4-Item-Concepts.md: -------------------------------------------------------------------------------- 1 | # Destiny 2 API Introduction - Part 4 - Item Concepts 2 | 3 | ## Introduction 4 | 5 | In Parts 1-3 we introduced the Destiny 2 API and the game manifest. Another key concept in the API is items. The API treats items in an abstract fashion so an item doesn't just mean a normal in-game character item like a helmet or shotgun. In this article we'll go over some of the general item concepts. 6 | 7 | 8 | ## Base items and Instanced Items 9 | 10 | First off there is the concept of the base item and an instanced item. The base item is a static representation of the item in the game and it is defined by the manifest entry for that item. This is the "stock" version of the item and defines the base stats of the item with the default perks selected (if applicable), default shader (if applicable), etc. Then there is an instanced item. This is the specific copy of the base item associated with a particular account - with the player chosen perks selected, shader, mods, etc. applied. Instanced item details are provided by the API, and while the information about an instanced items may still reference the manifest - a view of an instanced item from the API is a point-in-time view of the specific item associated with that player account. One second after you get your API response the player could apply a new shader and your point-in-time view of the item would then be out of date, for example. 11 | 12 | 13 | ## General Items 14 | 15 | The general type of items are the ones shown in the character overview screen: 16 | 17 | Kinetic weapons 18 | Energy weapons 19 | Power weapons 20 | Ghost shells 21 | Helmets 22 | Gauntlets 23 | Chest armor 24 | Leg armor 25 | Class items 26 | Sparrows 27 | Ships 28 | Emblems 29 | Emotes 30 | 31 | Kinetic and Energy weapons include Auto Rifle, Hand Cannon, Pulse Rifle, Scout Rifle, Sidearm, Submachine gun, and Trace Rifle. 32 | 33 | Power weapons include Fusion rifle, Grenade launcher, Linear fusion rifle, Rocket launcher, Sniper Rifle, and Sword. 34 | 35 | Item tiers include Basic (white), Common (green), Rare (blue), Legendary (purple), and Exotic (yellow). 36 | 37 | Different items may have progression requirements like a minimum required level to equip. Players may receive items in-game when they don't meet a given item's requirements, but they won't be able to equip them until the requirements are met. 38 | 39 | 40 | ## Other items 41 | 42 | There are lots of other items in Destiny 2. Again the general types are shown in the character overview screen: 43 | 44 | Engrams 45 | Consumables 46 | Mods (including Ornaments) 47 | Shaders 48 | 49 | Each of these item types consist of dozens of different items. Engrams holds found engrams of the various tiers that require decryption by a Cryptarch. Consumables has planetary materials, Gunsmith parts, reputation tokens, etc. Mods consist of weapon and armor mods, weapon and armor ornaments, trasmat effects, etc. Shaders work essentially like a mod and change the coloration of the item. 50 | 51 | There are also Auras which are status effects applied to a character for completing a certain activity like the Prestige Nightfall, and Clan Banners which are given to players who choose to join a clan. 52 | 53 | 54 | ## Item Buckets 55 | 56 | As you can see in the player interface different items are displayed in the user interface in different sections. For example the on-character Kinetic weapon section of the character screen. That Kinetic weapon section is called a bucket. Items are stored in various buckets of different sizes, for example the on-character Kinetic weapon section allows one equipped item and nine un-equipped items. Sometimes these buckets are used to hold special items like Quests, for example the Mida Multi-Tool exotic scout rifle quest will be stored in the Kinetic weapon bucket. The Vault and Postmaster are other examples of buckets. 57 | 58 | 59 | ## Base items and the Manifest 60 | 61 | Most base items are defined in the game manifest in the DestinyInventoryItemDefinition table. Item tiers are defined in the DestinyItemTierTypeDefinition table. Item damage types are defined in the DestinyDamageTypeDefinition table. Item stats are defined in the DestinyStatDefinition table. Item buckets are defined in the DestinyInventoryBucketDefinition. 62 | 63 | 64 | ## Items you might not think are items 65 | 66 | The Destiny 2 API treats many things as items which you might not think are items. Subclass abilities, perks, weapon sights, missions, and adventures are a few. If you are wondering where the definition of something is in the manifest and there doesn't appear to be a clear reference like classHash pointing to DestinyClassDefinition, then DestinyInventoryItemDefinition is often a good place to start. 67 | 68 | 69 | This article covered some introductory item concepts. In the next article we'll use the API to exercise some of these concepts. -------------------------------------------------------------------------------- /API-Introduction-Part-5-Item-Examples.md: -------------------------------------------------------------------------------- 1 | # Destiny 2 API Introduction - Part 5 - Items 2 | 3 | ## Introduction 4 | 5 | In this article we will exercise some of the item concepts from Part 4. As this is an article series for beginners we will stick to some introductory examples to give an idea of how things work. The API responses for items can be very large so sometimes I'll be showing snippets of the responses which focus on the topic being discussed - this will be indicated with {snip}. 6 | 7 | ## Inventory Example 8 | 9 | Going back to our example player Dattowatto we can see the equipped items of that character by choosing a different component from Destiny2.GetCharacter than we did in Part 2. 10 | 11 | https://www.bungie.net/Platform/Destiny2/2/Profile/4611686018428389623/Character/2305843009262373961/?components=205 12 | 13 | { 14 | "Response": { 15 | "equipment": { 16 | "data": { 17 | "items": [ 18 | { 19 | "itemHash": 1960218487, 20 | "itemInstanceId": "6917529040022201988", 21 | "quantity": 1, 22 | "bindStatus": 0, 23 | "location": 1, 24 | "bucketHash": 1498876634, 25 | "transferStatus": 1, 26 | "lockable": true, 27 | "state": 0 28 | }, 29 | { 30 | "itemHash": 3854359821, 31 | "itemInstanceId": "6917529035517937640", 32 | "quantity": 1, 33 | "bindStatus": 0, 34 | "location": 1, 35 | "bucketHash": 2465295065, 36 | "transferStatus": 1, 37 | "lockable": true, 38 | "state": 1 39 | }, 40 | { 41 | "itemHash": 1508896098, 42 | "itemInstanceId": "6917529034078959643", 43 | "quantity": 1, 44 | "bindStatus": 0, 45 | "location": 1, 46 | "bucketHash": 953998645, 47 | "transferStatus": 1, 48 | "lockable": true, 49 | "state": 1 50 | }, 51 | { 52 | "itemHash": 1804445917, 53 | "itemInstanceId": "6917529038205026946", 54 | "quantity": 1, 55 | "bindStatus": 0, 56 | "location": 1, 57 | "bucketHash": 3448274439, 58 | "transferStatus": 1, 59 | "lockable": true, 60 | "state": 1 61 | }, 62 | { 63 | "itemHash": 691332172, 64 | "itemInstanceId": "6917529038485499802", 65 | "quantity": 1, 66 | "bindStatus": 0, 67 | "location": 1, 68 | "bucketHash": 3551918588, 69 | "transferStatus": 1, 70 | "lockable": true, 71 | "state": 1 72 | }, 73 | { 74 | "itemHash": 3865618708, 75 | "itemInstanceId": "6917529038562390852", 76 | "quantity": 1, 77 | "bindStatus": 0, 78 | "location": 1, 79 | "bucketHash": 14239492, 80 | "transferStatus": 1, 81 | "lockable": true, 82 | "state": 1 83 | }, 84 | { 85 | "itemHash": 1337167606, 86 | "itemInstanceId": "6917529038179232715", 87 | "quantity": 1, 88 | "bindStatus": 0, 89 | "location": 1, 90 | "bucketHash": 20886954, 91 | "transferStatus": 1, 92 | "lockable": true, 93 | "state": 1 94 | }, 95 | { 96 | "itemHash": 1197579132, 97 | "itemInstanceId": "6917529029542792933", 98 | "quantity": 1, 99 | "bindStatus": 0, 100 | "location": 1, 101 | "bucketHash": 1585787867, 102 | "transferStatus": 1, 103 | "lockable": true, 104 | "state": 0 105 | }, 106 | { 107 | "itemHash": 997920961, 108 | "itemInstanceId": "6917529031221707329", 109 | "quantity": 1, 110 | "bindStatus": 0, 111 | "location": 1, 112 | "bucketHash": 4023194814, 113 | "transferStatus": 1, 114 | "lockable": true, 115 | "state": 0 116 | }, 117 | { 118 | "itemHash": 807458183, 119 | "itemInstanceId": "6917529029236363498", 120 | "quantity": 1, 121 | "bindStatus": 0, 122 | "location": 1, 123 | "bucketHash": 2025709351, 124 | "transferStatus": 1, 125 | "lockable": true, 126 | "state": 0 127 | }, 128 | { 129 | "itemHash": 96858972, 130 | "itemInstanceId": "6917529040028065789", 131 | "quantity": 1, 132 | "bindStatus": 0, 133 | "location": 1, 134 | "bucketHash": 284967655, 135 | "transferStatus": 1, 136 | "lockable": true, 137 | "state": 0 138 | }, 139 | { 140 | "itemHash": 2958378809, 141 | "itemInstanceId": "6917529029206452268", 142 | "quantity": 1, 143 | "bindStatus": 0, 144 | "location": 1, 145 | "bucketHash": 3284755031, 146 | "transferStatus": 3, 147 | "lockable": false, 148 | "state": 0 149 | }, 150 | { 151 | "itemHash": 3115055261, 152 | "itemInstanceId": "6917529035710821166", 153 | "quantity": 1, 154 | "bindStatus": 0, 155 | "location": 1, 156 | "bucketHash": 4274335291, 157 | "transferStatus": 3, 158 | "lockable": true, 159 | "state": 0 160 | }, 161 | { 162 | "itemHash": 1954221115, 163 | "itemInstanceId": "6917529029213981834", 164 | "quantity": 1, 165 | "bindStatus": 0, 166 | "location": 1, 167 | "bucketHash": 3054419239, 168 | "transferStatus": 3, 169 | "lockable": true, 170 | "state": 0 171 | }, 172 | { 173 | "itemHash": 3282648591, 174 | "itemInstanceId": "6917529047358959675", 175 | "quantity": 1, 176 | "bindStatus": 0, 177 | "location": 1, 178 | "bucketHash": 1269569095, 179 | "transferStatus": 3, 180 | "lockable": false, 181 | "state": 0 182 | } 183 | ] 184 | }, 185 | "privacy": 1 186 | }, 187 | "itemComponents": {} 188 | }, 189 | "ErrorCode": 1, 190 | "ThrottleSeconds": 0, 191 | "ErrorStatus": "Success", 192 | "Message": "Ok", 193 | "MessageData": {} 194 | } 195 | 196 | You can see that even this basic equipped items query returns a pretty large response. Lets take a look at the first object in Response.equipment.data.items: 197 | 198 | { 199 | "itemHash": 1960218487, 200 | "itemInstanceId": "6917529040022201988", 201 | "quantity": 1, 202 | "bindStatus": 0, 203 | "location": 1, 204 | "bucketHash": 1498876634, 205 | "transferStatus": 1, 206 | "lockable": true, 207 | "state": 0 208 | } 209 | 210 | The key items in this object are bucketHash, itemHash, and itemInstanceId. The bucketHash tells us about the item type, the itemHash tells us about the base item, and itemInstanceHash tells us about the instanced item - concepts we went over in the Part 4. 211 | 212 | 213 | ## Static/Base Item Information 214 | 215 | So using the base item information above we query the game manifest table DestinyInventoryBucketDefinition for bucketHash: 1498876634 using the techniques described in Part 3. 216 | 217 | { 218 | "displayProperties": { 219 | "description": "Weapons that deal kinetic damage. Most effective when dealing with unshielded enemies.", 220 | "name": "Kinetic Weapons", 221 | "hasIcon": false 222 | }, 223 | "scope": 0, 224 | "category": 3, 225 | "bucketOrder": 20, 226 | "itemCount": 10, 227 | "location": 1, 228 | "hasTransferDestination": true, 229 | "enabled": true, 230 | "fifo": false, 231 | "hash": 1498876634, 232 | "index": 0, 233 | "redacted": false, 234 | "blacklisted": false 235 | } 236 | 237 | Here we can see that this item is a Kinetic Weapon. 238 | 239 | Then we query the manifest table DestinyInventoryItemDefinition for itemHash: 1960218487. The following is a heavily snipped version of the response since it is over 700 lines normally and we'll concentrate on the basics in this first example. 240 | 241 | { 242 | "displayProperties": { 243 | "description": "Strange things wake at the stroke of twelve.", 244 | "name": "Nameless Midnight", 245 | "icon": "/common/destiny2_content/icons/535cb2c4046bec8ca7af199babef875d.jpg", 246 | "hasIcon": true 247 | }, 248 | "backgroundColor": { 249 | {snip} 250 | }, 251 | "screenshot": "/common/destiny2_content/screenshots/1960218487.jpg", 252 | "itemTypeDisplayName": "Scout Rifle", 253 | "uiItemDisplayStyle": "", 254 | "itemTypeAndTierDisplayName": "Legendary Scout Rifle", 255 | "displaySource": "", 256 | "action": { 257 | {snip} 258 | }, 259 | "inventory": { 260 | "maxStackSize": 1, 261 | "bucketTypeHash": 1498876634, 262 | "recoveryBucketTypeHash": 215593132, 263 | "tierTypeHash": 4008398120, 264 | "isInstanceItem": true, 265 | "nonTransferrableOriginal": false, 266 | "tierTypeName": "Legendary", 267 | "tierType": 5 268 | }, 269 | "stats": { 270 | {snip} 271 | }, 272 | "equippingBlock": { 273 | {snip} 274 | }, 275 | "translationBlock": { 276 | {snip} 277 | }, 278 | "quality": { 279 | {snip} 280 | }, 281 | "sourceData": { 282 | {snip} 283 | }, 284 | "acquireRewardSiteHash": 0, 285 | "acquireUnlockHash": 0, 286 | "sockets": { 287 | {snip} 288 | }, 289 | "talentGrid": { 290 | {snip} 291 | }, 292 | "investmentStats": [ 293 | {snip} 294 | ], 295 | "perks": [], 296 | "summaryItemHash": 3520001075, 297 | "allowActions": true, 298 | "doesPostmasterPullHaveSideEffects": false, 299 | "nonTransferrable": false, 300 | "itemCategoryHashes": [ 301 | 2, 302 | 8, 303 | 1 304 | ], 305 | "specialItemType": 0, 306 | "itemType": 3, 307 | "itemSubType": 14, 308 | "classType": 3, 309 | "equippable": true, 310 | "damageTypeHashes": [], 311 | "damageTypes": [ 312 | 1 313 | ], 314 | "defaultDamageType": 1, 315 | "defaultDamageTypeHash": 3373582085, 316 | "hash": 1960218487, 317 | "index": 1707, 318 | "redacted": false, 319 | "blacklisted": false 320 | } 321 | 322 | So now we know it is a Kinetic Weapon, and it is the Legendary Scout Rifle named Nameless Midnight. These two manifest queries give us the base information about the item type and item. In review the base item is the "stock" version. The instanced item is the specific copy of that item associated with the player account. The instanced item information is obtained from the API using the previously noted itemInstanceId: 6917529040022201988. 323 | 324 | 325 | ## Instanced Item Information 326 | 327 | To get instanced item information we use the Destiny2.GetItem endpoint with the player account information (membershipType and membershipID) as well as the specific itemInstanceId. This endpoint also uses components to determine which information is desired. 328 | 329 | https://www.bungie.net/Platform/Destiny2/2/Profile/4611686018428389623/Item/6917529040022201988/?components=300,302,304,305 330 | 331 | 332 | { 333 | "Response": { 334 | "characterId": "2305843009262373961", 335 | "instance": { 336 | "data": { 337 | "damageType": 1, 338 | "damageTypeHash": 3373582085, 339 | "primaryStat": { 340 | "statHash": 1480404414, 341 | "value": 305, 342 | "maximumValue": 0 343 | }, 344 | "itemLevel": 30, 345 | "quality": 0, 346 | "isEquipped": true, 347 | "canEquip": true, 348 | "equipRequiredLevel": 20, 349 | "unlockHashesRequiredToEquip": [ 350 | 2166136261 351 | ], 352 | "cannotEquipReason": 0 353 | }, 354 | "privacy": 1 355 | }, 356 | "perks": { 357 | "data": { 358 | "perks": [ 359 | { 360 | "perkHash": 4040885430, 361 | "iconPath": "/common/destiny2_content/icons/e14a612c871f23c784e182d363f120a8.png", 362 | "isActive": true, 363 | "visible": true 364 | }, 365 | { 366 | "perkHash": 3114011448, 367 | "iconPath": "", 368 | "isActive": true, 369 | "visible": false 370 | }, 371 | { 372 | "perkHash": 2666547425, 373 | "iconPath": "/common/destiny2_content/icons/c2fe5ad080d1dcd3cf97674f71ecb3a6.png", 374 | "isActive": true, 375 | "visible": true 376 | } 377 | ] 378 | }, 379 | "privacy": 1 380 | }, 381 | "stats": { 382 | "data": { 383 | "stats": { 384 | "155624089": { 385 | "statHash": 155624089, 386 | "value": 49, 387 | "maximumValue": 100 388 | }, 389 | "943549884": { 390 | "statHash": 943549884, 391 | "value": 56, 392 | "maximumValue": 100 393 | }, 394 | "1240592695": { 395 | "statHash": 1240592695, 396 | "value": 51, 397 | "maximumValue": 100 398 | }, 399 | "3871231066": { 400 | "statHash": 3871231066, 401 | "value": 16, 402 | "maximumValue": 100 403 | }, 404 | "4043523819": { 405 | "statHash": 4043523819, 406 | "value": 62, 407 | "maximumValue": 100 408 | }, 409 | "4188031367": { 410 | "statHash": 4188031367, 411 | "value": 99, 412 | "maximumValue": 100 413 | }, 414 | "4284893193": { 415 | "statHash": 4284893193, 416 | "value": 180, 417 | "maximumValue": 100 418 | } 419 | } 420 | }, 421 | "privacy": 1 422 | }, 423 | "sockets": { 424 | "data": { 425 | "sockets": [ 426 | { 427 | "plugHash": 1636108362, 428 | "isEnabled": true, 429 | "reusablePlugHashes": [ 430 | 1636108362 431 | ] 432 | }, 433 | { 434 | "plugHash": 2405638015, 435 | "isEnabled": true, 436 | "reusablePlugHashes": [ 437 | 2405638015, 438 | 2405638014, 439 | 679077872 440 | ] 441 | }, 442 | { 443 | "plugHash": 3230963543, 444 | "isEnabled": true, 445 | "reusablePlugHashes": [ 446 | 3230963543, 447 | 3177308360 448 | ] 449 | }, 450 | { 451 | "plugHash": 3038247973, 452 | "isEnabled": true, 453 | "reusablePlugHashes": [ 454 | 3038247973 455 | ] 456 | }, 457 | { 458 | "plugHash": 4207478320, 459 | "isEnabled": true 460 | }, 461 | { 462 | "isEnabled": false 463 | }, 464 | { 465 | "plugHash": 4248210736, 466 | "isEnabled": true, 467 | "reusablePlugHashes": [ 468 | 4248210736 469 | ] 470 | }, 471 | { 472 | "plugHash": 236077174, 473 | "isEnabled": true, 474 | "reusablePlugHashes": [ 475 | 1961001474, 476 | 3612467353, 477 | 2946649456, 478 | 2667900317 479 | ] 480 | } 481 | ] 482 | }, 483 | "privacy": 1 484 | } 485 | }, 486 | "ErrorCode": 1, 487 | "ThrottleSeconds": 0, 488 | "ErrorStatus": "Success", 489 | "Message": "Ok", 490 | "MessageData": {} 491 | } 492 | 493 | We provided the account for which this instanced item was associated and we were returned the characterId which is currently holding the item - and sure enough it matches the characterId that we provided when we checked the API for that character's inventory. So the player hasn't moved this gun in the mean time. 494 | 495 | You can see this response has general information (like damage type and the current Power Level of the item), perks, stats, and sockets. 496 | 497 | We know this is a Kinetic weapon from the bucketHash but we can also see that the damageTypeHash: 3373582085 which if we check in the DestinyDamageTypeDefinition table of the manifest corresponds to Kinetic. With Energy and Power weapons the player may have changed the damage type with a mod so the damageTypeHash will show the current damage type versus what "stock" damage type may be associated with the base item in the manifest. 498 | 499 | Items like weapons and armor have a details screen in the game interface. This screen shows a combination of the item's perks and sockets. Sockets compose a number of different concepts like "selectable perks" (versus fixed perks), mods, shaders, ornaments, etc. "Plugs" can be inserted into "sockets" to activate that behavior. So in this concept a weapon may have a shader socket and the shader itself is a plug that can be used to activate the shader on that item. 500 | 501 | An item's stats may be impacted by the specific perks and sockets used like changing the weapon sight might change range and/or handling. An instanced item response shows the stats of the item with the specificly presented set of options configured - so these stats may differ from the stats provided in the base item (since those are the base stats with the default options). 502 | 503 | So let's look at the two visible perks on this weapon by looking up the perkHashes in the DestinySandboxPerkDefinition table of the manifest: 504 | 505 | { 506 | "displayProperties": { 507 | "description": "PRECISION \nRecoil pattern is more vertical.", 508 | "name": "Precision Frame", 509 | "icon": "/common/destiny2_content/icons/e14a612c871f23c784e182d363f120a8.png", 510 | "hasIcon": true 511 | }, 512 | "isDisplayable": true, 513 | "damageType": 0, 514 | "hash": 4040885430, 515 | "index": 328, 516 | "redacted": false, 517 | "blacklisted": false 518 | } 519 | 520 | { 521 | "displayProperties": { 522 | "description": "Projectiles create an area-of-effect detonation on impact.", 523 | "name": "Explosive Payload", 524 | "icon": "/common/destiny2_content/icons/c2fe5ad080d1dcd3cf97674f71ecb3a6.png", 525 | "hasIcon": true 526 | }, 527 | "isDisplayable": true, 528 | "damageType": 0, 529 | "hash": 2666547425, 530 | "index": 295, 531 | "redacted": false, 532 | "blacklisted": false 533 | } 534 | 535 | We can see this is a Precision Frame scout rifle with the Explosive Payload attribute. Both of these are "fixed perks". 536 | 537 | Then let's look at one of the sockets objects: 538 | 539 | { 540 | "plugHash": 2405638015, 541 | "isEnabled": true, 542 | "reusablePlugHashes": [ 543 | 2405638015, 544 | 2405638014, 545 | 679077872 546 | ] 547 | } 548 | 549 | We can see this socket has three reusablePlugHashes so this is a "selectable perk". We can also see which one of these three options is currently selected with plugHash (2405638015). So we lookup the plugHash in the DestinyInventoryItemDefinition table of the manifest. 550 | 551 | { 552 | "displayProperties": { 553 | "description": "Snapshot sight. Short zoom. \n • Slightly increases range\n • Increases handling speed", 554 | "name": "Red Dot 2 MOA", 555 | "icon": "/common/destiny2_content/icons/ce9748fa06f67f655c2f1addae333f19.png", 556 | "hasIcon": true 557 | }, 558 | "backgroundColor": { 559 | {snip} 560 | }, 561 | "itemTypeDisplayName": "Scope", 562 | "uiItemDisplayStyle": "", 563 | "itemTypeAndTierDisplayName": "Legendary Scope", 564 | "displaySource": "", 565 | "action": { 566 | {snip} 567 | }, 568 | "inventory": { 569 | {snip} 570 | }, 571 | "plug": { 572 | {snip} 573 | }, 574 | {snip} 575 | "hash": 2405638015, 576 | "index": 2017, 577 | "redacted": false, 578 | "blacklisted": false 579 | } 580 | 581 | So we can see this socket is for the sight selections on the Nameless Midnight, and the player has selected the "Red Dot 2 MOA" sight (2405638015). While the other two plugHashes correspond to the "Red Dot Micro" (2405638014) and the "Rifle Scope SFF" (679077872). 582 | 583 | And finally lets look at an example stats object: 584 | 585 | "1240592695": { 586 | "statHash": 1240592695, 587 | "value": 51, 588 | "maximumValue": 100 589 | } 590 | 591 | We have a statHash: 1240592695 so we use the DestinyStatDefinition table of the manifest which returns: 592 | 593 | { 594 | "displayProperties": { 595 | "description": "Increases the effective range of this weapon.", 596 | "name": "Range", 597 | "icon": "/img/misc/missing_icon_d2.png", 598 | "hasIcon": false 599 | }, 600 | "aggregationType": 2, 601 | "hasComputedBlock": false, 602 | "interpolate": false, 603 | "hash": 1240592695, 604 | "index": 15, 605 | "redacted": false, 606 | "blacklisted": false 607 | } 608 | 609 | We can see that statHash is for Range so this instanced item of the Nameless Midnight has a range value of 51. 610 | 611 | 612 | Again there are many different kinds of items in Destiny 2 and not all of them follow this example exactly, but this overview provides examples of many of the item concepts that will apply to the most commonly performed item queries. 613 | -------------------------------------------------------------------------------- /API-Introduction-Part-X-Other-Resources.md: -------------------------------------------------------------------------------- 1 | # Destiny 2 API Introduction - Part X - Other resources 2 | 3 | ## Introduction 4 | 5 | There are other places that contain good content for Destiny community developers and I'll list some of them here to make them easier to find. 6 | 7 | ## Other Resources 8 | 9 | [Official Bungie API github](https://github.com/Bungie-net/api) 10 | 11 | [Destiny API Discussion Discord server invite link](https://discord.gg/HU3Q4up) 12 | 13 | [DestinyDevs unofficial Wiki](http://destinydevs.github.io/BungieNetPlatform/) 14 | 15 | [Destiny API Wiki by xlxCLUxlx](https://github.com/xlxCLUxlx/Destiny2API/wiki) 16 | -------------------------------------------------------------------------------- /Clan-Banner-Images.md: -------------------------------------------------------------------------------- 1 | The Bungie Clan Banner images are generated from multiple images. The real way to get these images can be found here https://github.com/xlxCLUxlx/Destiny2API/wiki/Building-Your-Clan-Banner:--How-To 2 | 3 | You can also use Selenium, an HTML parser, and a base64 decoder to "cheat" at getting the clan banner images from Bungie.net. You need Selenium because the images are generated via Javascript so a simple curl/wget won't find them. Here is a quick and dirty Python3.6 script to show the idea. 4 | 5 | #!/usr/bin/env python 6 | """ 7 | Get clan banner image from bungie.net 8 | 9 | Download Chrome driver from 10 | https://chromedriver.storage.googleapis.com/2.33/chromedriver_linux64.zip 11 | and unzip into working directory 12 | 13 | """ 14 | 15 | import base64 16 | from bs4 import BeautifulSoup 17 | from selenium import webdriver 18 | from selenium.webdriver.chrome.options import Options 19 | 20 | def main(): 21 | """ 22 | Use Selenium and BeautifulSoup for fun and profit 23 | """ 24 | 25 | options = Options() 26 | options.add_argument('--headless') 27 | options.add_argument('start-maximized') 28 | driver = webdriver.Chrome(executable_path='./chromedriver', chrome_options=options) 29 | driver.implicitly_wait(10) 30 | driver.get('https://www.bungie.net/en/ClanV2?groupId=2080464') 31 | html = driver.execute_script('return document.body.innerHTML') 32 | driver.close() 33 | soup = BeautifulSoup(html, 'html.parser') 34 | bdiv = soup.find('div', {'id': 'ClanBanner'}) 35 | img = bdiv['style'] 36 | img = img.replace('background-image: url("data:image/png;base64,', '') 37 | img = img.replace('");', '') 38 | with open('clanbanner.png', 'wb') as outfile: 39 | outfile.write(base64.urlsafe_b64decode(img)) 40 | 41 | if __name__ == '__main__': 42 | main() 43 | -------------------------------------------------------------------------------- /Destiny-2-Open-Source-Projects.md: -------------------------------------------------------------------------------- 1 | ## Destiny 2 API Information, Tools, and Projects (by vpzed) 2 | 3 | [the-traveler npm package for D2 API](https://github.com/alexanderwe/the-traveler) 4 | 5 | [pydest Python package for D2 API](https://github.com/jgayfer/pydest) 6 | 7 | [PHP package for D2 API](https://github.com/adamdburton/destiny-2-api-client) 8 | 9 | [C# libraries for D2 API](https://github.com/BaileyMillerSSI/Destiny2Api) 10 | 11 | [DestinyDashboard.net github](https://github.com/lax20attack/destiny-dashboard) 12 | 13 | [DestinyItemManager.com github](https://github.com/DestinyItemManager/DIM) 14 | 15 | [DestinyStatus.com github](https://github.com/TrackerNetwork/DestinyStatus) 16 | 17 | [Destinder.com github](https://github.com/destiny-aviato/destinder) 18 | 19 | [Guardian Helper Alexa skill github](https://github.com/rking788/guardian-helper) 20 | 21 | [DestinyLoadouts Alexa skill github](https://github.com/kissellj/DestinyLoadouts) 22 | 23 | [destiny.plumbing github](https://github.com/joshhunt/destinyPlumbing) 24 | 25 | [destinySets github](https://github.com/joshhunt/destinySets) 26 | 27 | [destiny-gotg clan stats and Discord bot](https://github.com/adherrling/destiny-gotg) 28 | 29 | [Guardian.Theater github](https://github.com/chrisfried/guardian-theater-ng2) 30 | 31 | [7SAIF github org with multiple Destiny projects](https://github.com/7SAIF) 32 | 33 | [Python Social Auth github which now supports Bungie Oauth](https://github.com/python-social-auth/) 34 | 35 | [Destiny-Daily-2 github](https://github.com/mattybeard/Destiny-Daily-2) 36 | 37 | [DestinyCollectionManager github](https://github.com/mattybeard/DestinyCollectionManager) 38 | -------------------------------------------------------------------------------- /Home.md: -------------------------------------------------------------------------------- 1 | Welcome to the Destiny2-API-Info Wiki 2 | 3 | This is a place that I'll be saving information I create and/or find about the Destiny 2 API, as well as articles submitted by other Destiny 2 developers. 4 | 5 | If you want to have an article posted just create a Markdown Gist and send me the link. I'll add it with credit to your GitHub username as the author in the article title. 6 | 7 | If you found this looking for the Bungie project, here is [official Bungie API page](https://github.com/Bungie-net/api). 8 | -------------------------------------------------------------------------------- /Leviathan-Raid-Encounter-Rotation-Info.md: -------------------------------------------------------------------------------- 1 | ## Leviathan Raid Encounter Rotation Info 2 | 3 | NOTE: The Leviathan Raid Encounter Rotation information was provided by **Wayno156** on the Destiny API Discussion Discord server. This article by vpzed is an explanation around the information **he provided**. 4 | 5 | The Leviathan Raid has two modes (Normal and Prestige) and the intermediary encounter order rotates each week. At the time of this writing the Power Level of the Normal version is 300 and the Prestige version is 330, so another way to look at the table below is Normal and Prestige since Power Levels change over time. 6 | 7 | By using the table below one can determine the weekly Leviathan raid encounter order from the activityHash information in the Milestone endpoint output (or other activityHash references). 8 | 9 | For example from this week's Milestone API response: 10 | 11 | "3660836525": { 12 | "milestoneHash": 3660836525, 13 | "availableQuests": [ 14 | { 15 | "questItemHash": 82550967, 16 | "activity": { 17 | "activityHash": 2693136604, 18 | "variants": [ 19 | { 20 | "activityHash": 2693136604, 21 | "activityModeHash": 2043403989, 22 | "activityModeType": 4 23 | }, 24 | { 25 | "activityHash": 757116822, 26 | "activityModeHash": 2043403989, 27 | "activityModeType": 4 28 | } 29 | 30 | We can see the activityHashes for the Leviathan Raid (activityModeType: 4 is Raid) are 2693136604 and 757116822. 31 | 32 | So from the table below we can see that the encounter order is Gauntlet, Royal Pools, Pleasure Gardens. 33 | 34 | 35 | hash power detail 36 | 2693136605 300 Gauntlet, Pleasure Gardens, Royal Pools 37 | 2693136604 300 Gauntlet, Royal Pools, Pleasure Gardens 38 | 2693136602 300 Pleasure Gardens, Gauntlet, Royal Pools 39 | 2693136603 300 Pleasure Gardens, Royal Pools, Gauntlet 40 | 2693136600 300 Royal Pools, Gauntlet, Pleasure Gardens 41 | 2693136601 300 Royal Pools, Pleasure Gardens, Gauntlet 42 | 1685065161 330 Gauntlet, Pleasure Gardens, Royal Pools 43 | 757116822 330 Gauntlet, Royal Pools, Pleasure Gardens 44 | 417231112 330 Pleasure Gardens, Gauntlet, Royal Pools 45 | 3446541099 330 Pleasure Gardens, Royal Pools, Gauntlet 46 | 2449714930 330 Royal Pools, Gauntlet, Pleasure Gardens 47 | 3879860661 330 Royal Pools, Pleasure Gardens, Gauntlet 48 | 49 | 50 | NOTE: To be clear this raid encounter order information was compiled by Wayno156, and this information is not from the Bungie API directly. To use this in your application you would create a lookup method in your application to do the de-referencing. 51 | -------------------------------------------------------------------------------- /Locations-of-Specific-Game-Information-in-the-API.md: -------------------------------------------------------------------------------- 1 | ## Locations of Specific Game Information in the API (by kbreit) 2 | 3 | Table list as of 2017/10/12: 4 | 5 | * Nightfall 6 | * Flashpoint 7 | * Iron Banner 8 | 9 | ### Nightfall 10 | Endpoint: https://www.bungie.net/Platform/Destiny2/Milestones/ 11 | 12 | JSON Path: Response.2171429505.availableQuests.0.activity 13 | 14 | Components: 15 | * Event name 16 | * Modifiers 17 | * Challenges 18 | * Beginning and ending dates 19 | 20 | ### Flashpoint 21 | Endpoint: https://www.bungie.net/Platform/Destiny2/Milestones/ 22 | 23 | JSON Path: Response.463010297.availableQuests 24 | 25 | Components: 26 | * Location 27 | * Beginning and ending dates 28 | 29 | ### Iron Banner 30 | Endpoint: https://www.bungie.net/Platform/Destiny2/Milestones/ 31 | 32 | JSON Path: Response.4248276869.milestoneHash 33 | 34 | Components: 35 | * Milestone goal (ex. Complete 10 Iron Banner ranks) 36 | * Daily objectives -------------------------------------------------------------------------------- /Manifest-Queries-with-JSON1-Extension.md: -------------------------------------------------------------------------------- 1 | ## SQLite Queries using the JSON1 extension (by vpzed) 2 | 3 | NOTE: These require a SQLite query program with the JSON1 extensions. These work fine in "DB Browser for SQLite" for example. 4 | 5 | As of the 2017/09/13 Manifest all tables use the numeric "hash" property as a unique identifier inside the 'json' column, 6 | except DestinyHistoricalStatsDefinition which uses the string "statId" property. 7 | 8 | Using the JSON1 extension you can bypass using the 'id' or 'key' columns of the manifest and query the 'json' columns of the tables directly. This means you can query directly using what is received from the API response instead of having to transform it like with "hash" to 'id'. 9 | 10 | Returns all properties on the matched row and use an exact match on the hash: 11 | 12 | SELECT json_extract(DestinyRaceDefinition.json, '$') 13 | FROM DestinyRaceDefinition, json_tree(DestinyRaceDefinition.json, '$') 14 | WHERE json_tree.key = 'hash' AND json_tree.value = 2803282938 15 | 16 | Return only the displayProperties.name property on the row and use an exact match on the hash: 17 | 18 | SELECT json_extract(DestinyRaceDefinition.json, '$.displayProperties.name') 19 | FROM DestinyRaceDefinition, json_tree(DestinyRaceDefinition.json, '$') 20 | WHERE json_tree.key = 'hash' AND json_tree.value = 2803282938 21 | 22 | Case insensitive search for a "keyword" in the name property: 23 | 24 | SELECT json_extract(DestinyActivityModeDefinition.json, '$') 25 | FROM DestinyActivityModeDefinition, json_tree(DestinyActivityModeDefinition.json, '$') 26 | WHERE json_tree.key = 'name' AND UPPER(json_tree.value) LIKE UPPER('%strike%') 27 | 28 | Return rows that match a list of values: 29 | 30 | SELECT json_extract(DestinyInventoryItemDefinition.json, '$') 31 | FROM DestinyInventoryItemDefinition, json_tree(DestinyInventoryItemDefinition.json, '$') 32 | WHERE json_tree.key = 'itemType' AND json_tree.value IN (2,3,4) 33 | 34 | Return rows that match a Boolean value of "true": 35 | 36 | SELECT json_extract(DestinyActivityDefinition.json, '$.hash') as hash 37 | FROM DestinyActivityDefinition, json_tree(DestinyActivityDefinition.json) 38 | WHERE json_tree.key = 'redacted' AND json_tree.value 39 | 40 | Return specified columns and search an array property for a specific included value: 41 | 42 | SELECT json_extract(DestinyInventoryItemDefinition.json, '$.displayProperties.name'), 43 | json_extract(DestinyInventoryItemDefinition.json, '$.displayProperties.icon') 44 | FROM DestinyInventoryItemDefinition, 45 | json_each(DestinyInventoryItemDefinition.json, '$.itemCategoryHashes') 46 | WHERE json_each.value LIKE '56'; -------------------------------------------------------------------------------- /_Footer.md: -------------------------------------------------------------------------------- 1 | ### Want to contribute to this Wiki? 2 | 3 | [Fork the linked repo and send a pull request.](https://github.com/vpzed/Destiny2-API-Info-wiki) 4 | 5 | --------------------------------------------------------------------------------