Apiary Powered Documentation
Sign in with Apiary account.
We're looking for feedback! If you have any opinions or ideas, contact us on Slack.
Based on simple REST principles, the InBDPA API returns JSON data responses to requests. This is the API used by teams and their apps for the BDPA National High School Computer Competition. It contains all of the data teams' apps must interact with. The API is live and will ideally remain online indefinitely.
The base address of the InBDPA API is https://inbdpa.api.hscc.bdpa.org/V where V
is the version of the API you want to use. There is currently only one version, so V = v1
. Each version of the API provides a set of endpoints with their own unique path and requirements.
The source code behind the API is available on GitHub. If you have any trouble, open an issue there or contact us on Slack.
Notice: due to financial constraints, the oldest documents in the system will be dropped from the API to make room for the new. That is:
<item>_id
s are not guaranteed to exist forever!
To access the majority of this API's endpoints requires a key. If your team needs a key, or to replace a lost or stolen key, either use our Slack bot (BDPABot) to manage your team's keys or contact us on Slack.
When you get your key, include it as your request's Authorization header and you will be immediately authenticated into the system. For example: Authorization: bearer your-special-api-key-here
.
Do not bombard the API with requests or you risk permanent IP/subnet ban. Limit your apps to no more than 10 requests per second per API key. If your app ends up sending too many requests over some time period, you'll get a HTTP 429
response along with a monotonically increasing soft ban (starting at 15 minutes). Similarly, the size of requests is strictly limited, so you must limit the amount of data you're sending. When you send a request that is too large (>100KB), it will fail with a HTTP 413
response.
Do not reveal your API key to anyone not on your own team. It is how the API identifies your team. Do not upload it to GitHub or leave it lying around in your source code. Save it to a file and .gitignore
it or save it to an environment variable.
Since the API is live, you might be able to see or interact with content posted by other teams. If this is the case, please do not post anything inappropriate.
If you have a relevant feature request or you encounter any vulnerabilities, errors, or other issues, don't hesitate to contact NHSCC staff via Slack or open an issue on GitHub. For significant enough finds, bonus points may be awarded. On the other hand, abusing any vulnerability or bug may result in disqualification.
The API was built to randomly return errors every so often. That means your app must be prepared to deal with HTTP 555
and other bad responses. However, if you're consistently getting HTTP 5xx
errors back to back, then something is wrong. Please report this if it happens.
All responses are raw JSON. All request payloads must be sent as raw JSON. JSON.stringify()
and JSON.parse()
or whatever language equivalent is available to you is your friend!
This API is based on simple REST principles. Resources are accessed via standard HTTPS requests in UTF-8 format to an API endpoint. This API understands the following HTTP request methods:
METHOD | MEANING |
---|---|
GET | Return data about something |
POST | Create something new |
PATCH | Modify something |
PATCH | Partially modify something |
DELETE | Delete something |
As said earlier, do not bombard the API with requests. If you do, the API will soft ban you for fifteen minutes the first time before accepting requests from your API key or IP address again. Each following time this happens within a certain period, your ban time will quadruple.
So limit your apps to no more than 10 requests per second per API key. You know you've been soft banned if you receive an HTTP 429
response. Check the JSON response for the retryAfter
key, which represents for how long your API key and/or IP are banned from making further requests (in milliseconds).
If this is the first time you've been banned, you can use the Slack bot to unban yourself immediately. If the Slack bot is not available or this is not the first time you've been banned, contact us on Slack.
Endpoints that might return a lot of items (users, documents, etc) are paginated via range queries. Such endpoints optionally accept an after
parameter, which is an <item>_id
or other identifier that determines which API item is returned first. That is: the first item will be the first <item>_id
that comes after the after
<item>_id
. Omitting the after
parameter returns the first 100 items in the system.
For example, given the following dataset and an API with a default result size (or "page" size) of 3:
[
{ item_id: 0xabc123, name: 'Item 1 name' },
{ item_id: 0xabc124, name: 'Item 2 name' },
{ item_id: 0xabc125, name: 'Item 3 name' },
{ item_id: 0xabc126, name: 'Item 4 name' },
{ item_id: 0xabc127, name: 'Item 5 name' },
]
Suppose we issued the following requests to an API:
/api?after=0xabc123
: responds with an array of 3 items: 0xabc124 through 0xabc126
/api?after=0xabcXYZ
: responds with an array of 0 items since item_id
0xabcXYZ doesn't exist
/api?after=0xabc124
: responds with an array of 3 items: 0xabc125 through 0xabc127
/api?after=0xabc127
: responds with an array of 0 items since there is nothing after 0xabc127
/api?after=0xabc125
: responds with an array of 2 items: 0xabc126 and 0xabc127
This API will issue responses with one of the following status codes:
STATUS | MEANING |
---|---|
200 | Your request completed successfully. |
400 | Your request was malformed or otherwise bad. Check the requirements. |
401 | Session is not authenticated. Put your API key in the header! |
403 | Session is not authorized. You tried to do something you can't do. |
404 | The resource (or endpoint) was not found. Check your syntax. |
405 | Bad method. The endpoint does not support your request's method. |
413 | Your request was too large and was dropped. Max body size is 100KB. |
415 | Your request was made using the wrong Content-Type header value. |
429 | You've been rate limited. Try your request again after a few minutes. |
4xx | Your request was malformed in some way. |
5xx | Something happened on the server that is outside your control. |
All responses issued by the API will follow one of the two following schemas.
When a request you've issued succeeds, the response will look like the following:
{
"success": "true",
// any other data you requested
}
Note that all time data is represented as the number of milliseconds elapsed since January 1, 1970 00:00:00 UTC, or the same thing that is returned by JavaScript's Date.now()
method.
When a request you've issued fails, along with the non-200 status code, the response will look like the following:
{
"error": "an error message describing what went wrong",
// any other relevant data (like retryAfter)
}
The API has full support for Cross Origin Resource Sharing (CORS) for AJAX requests.
Are you using the right method?
Use this documentation (click "see example," then click "Try console") or use Postman to play with the API.
Expect a raw JSON response body that you must parse manually, not raw text or something else.
Are you sending properly formatted JSON payloads in your request body when necessary?
Try outputting to stdout, use console.log
, or output to some log file when API requests are made and responses received.
All time data is represented as the number of milliseconds elapsed since January 1, 1970 00:00:00 UTC.
Are you sending the correct headers? You need to specify the Authorization: bearer your-special-api-key-here
header for all requests and the 'content-type': 'application/json'
header when making POST and PATCH requests.
Are you encoding your URI components properly, especially when you're trying to send the API JSON objects via GET request?
To retrieve data about one or more API items, you must know that item's <item>_id
. These and other IDs are globally unique within the API. That is: no two items will ever have the same ID in any instance. Use this fact to your advantage.
These endpoints allow retrieval of statistics describing the entire system.
Get metadata about the entire system.
If the request succeeded. Always true
when status code is 200 and false
or undefined
otherwise.
These endpoints deal with CRUD operations on opportunities.
Retrieves all opportunities in the system in FIFO order.
Retrievals are limited to at most 100 results per query. Supports range queries using after
.
after | [optional] Return only those results that occur after |
---|---|
updatedAfter | [optional] Return only those users with |
If the request succeeded. Always true
when status code is 200 and false
or undefined
otherwise.
Creates a new opportunity.
"My Little Opportunity"
"5eee34b3ca37750008547375"
If the request succeeded. Always true
when status code is 200 and false
or undefined
otherwise.
Retrieve an opportunity by its opportunity_id
.
opportunity_id | [required] ID of the opportunity. |
---|
If the request succeeded. Always true
when status code is 200 and false
or undefined
otherwise.
Update an opportunity (opportunity_id
) in the system. The opportunity's updatedAt
timestamp is also updated.
opportunity_id | [required] ID of the opportunity. |
---|
"My Little Opportunity"
If the views should be incremented. If this property is present, it must have the value "increment"
.
"increment"
If the request succeeded. Always true
when status code is 200 and false
or undefined
otherwise.
Completely and permanently remove an opportunity from the system.
opportunity_id | [required] ID of the opportunity. |
---|
If the request succeeded. Always true
when status code is 200 and false
or undefined
otherwise.
These endpoints deal with CRUD operations on active viewer sessions.
Make the API aware of an active session, which represents one client interacting with one view (view
+ viewed_id
). Active sessions expire 30 seconds after their creation unless they are renewed, which will reset the 30-second timer.
Example usage: if user-A
clicks a link to user-B
's profile, which loads the Profile view, your solution would create a new session where view = 'profile'
and viewed_id = user-B's-user-id
. You would then continually renew that session every so often until user-A
navigates away from user-B
's profile.
Note that possible values for viewed_id
are: a user_id
(only if view === 'profile'
), an opportunity_id
(only if view === 'opportunity'
), or null
.
The view this session is associated with. Possible values are: "profile"
, "opportunity"
, "admin"
, "auth"
, or "home"
.
"profile"
A unique immutable MongoDB ID corresponding to the view
item associated with this session, or null
.
"5ec8adf06e38137ff2e58770"
If the request succeeded. Always true
when status code is 200 and false
or undefined
otherwise.
Renew an active session to indicate that a client (user) is still accessing a view. Renewing an active session resets the 30-second expiry period.
Hint: your solution should keep renewing the same session every so often until the client navigates away from the current view, closes the browser, etc. If the client navigates from one URL to another without changing views, your solution should still create a new session instead of renewing the existing one.
session_id | [required] ID of the session. |
---|
If the request succeeded. Always true
when status code is 200 and false
or undefined
otherwise.
Delete (manually expire) an active session.
session_id | [required] ID of the session. |
---|
If the request succeeded. Always true
when status code is 200 and false
or undefined
otherwise.
Returns the number of currently active sessions associated with the given user profile (user_id
).
Each active session represents one viewer that loaded this user's profile within the last 30 seconds. If an active session is not renewed within 30 seconds, it is automatically deleted.
user_id | [required] ID of the user. |
---|
If the request succeeded. Always true
when status code is 200 and false
or undefined
otherwise.
The number of active sessions associated with this user's profile.
Returns the number of currently active sessions associated with the given opportunity (opportunity_id
).
Each active session represents one viewer that loaded this opportunity's profile within the last 30 seconds. If an active session is not renewed within 30 seconds, it is automatically deleted.
opportunity_id | [required] ID of the opportunity. |
---|
If the request succeeded. Always true
when status code is 200 and false
or undefined
otherwise.
These endpoints deal with CRUD operations on users.
Retrieves all users in the system in FIFO order.
Retrievals are limited to at most 100 results per query. Supports range queries using after
.
after | [optional] Return only those results that occur after |
---|---|
updatedAfter | [optional] Return only those users with |
If the request succeeded. Always true
when status code is 200 and false
or undefined
otherwise.
Creates a new user.
Note that the API manages all user credentials. Passwords must NEVER be stored in any form ever (locally in your app or database or anywhere else), but are instead communicated as a special one-way "login key".
The Web Crypto API and Password-Based Key Derivation Function #2 (PBKDF2) must be used to derive this key. Here is an example using the Web Crypto API to derive a login key and salt from a password. Once this login key is derived, it and the salt must be sent to the API for storage.
NOTICE: all teams should use 100,000 iterations for PBKDF2 to make cross-app logins easier for the judges!
The user's unique username within the system. Must be lowercase alphanumeric (-
and _
are allowed).
"thehill"
The user's email address.
"h@hillaryclinton.com"
A 16-byte (32 characters) hex string representing a salt corresponding to the login key.
A 64-byte (128 characters) hex string representing a login key.
The type of this user. Possible values are: "inner"
, "staff"
, or "administrator"
.
"inner"
If the request succeeded. Always true
when status code is 200 and false
or undefined
otherwise.
Retrieve a user by their user_id
.
user_id | [required] ID of the user. |
---|
If the request succeeded. Always true
when status code is 200 and false
or undefined
otherwise.
Retrieve a user by their username
.
username | [required] username of the user. |
---|
If the request succeeded. Always true
when status code is 200 and false
or undefined
otherwise.
Update a user (user_id
) in the system. Can be used to change a user's password (i.e. their key
and salt
), their email, and other properties. See /users (POST) for more information on the derivation of a login key and salt.
The user's updatedAt
timestamp is also updated.
When updating a user's sections
, the patch will be applied at the section level. For example, submitting { "sections": { "about": "My new about page" }}
would overwrite the entire sections.about
section, but would not overwrite sections.education
, sections.volunteering
, or sections.skills
.
user_id | [required] ID of the user. |
---|
A 16-byte (32 characters) hex string representing an updated salt corresponding to the updated login key. Must be present if key
is present.
A 64-byte (128 characters) hex string representing an updated login key. Must be present if salt
is present.
The user's updated email address.
The user's updated section information.
The type of this user. Possible values are: "inner"
, "staff"
, or "administrator"
.
"inner"
If the views should be incremented. If this property is present, it must have the value "increment"
.
"increment"
If the request succeeded. Always true
when status code is 200 and false
or undefined
otherwise.
Completely and permanently remove a user from the system. Note that deleting a user will not delete their opportunities.
user_id | [required] ID of the user. |
---|
If the request succeeded. Always true
when status code is 200 and false
or undefined
otherwise.
Attempt to authenticate the credentials of a specific user.
To check if a given username (corresponding to user_id
) and password combination is valid, follow the same process as in the example to derive a login key using the salt accessible via the user endpoint. Send the newly derived login key to the API via this endpoint and, if it matches the key stored in the API, you will receive an HTTP 200
status code response. If the user credentials could not be authenticated, you will receive a HTTP 403
status code instead.
See /users (POST) for more information on the derivation of a login key and salt.
user_id | [required] ID of the user. |
---|
A 64-byte (128 characters) hex string representing a login key derived using PBKDF#2.
If the request succeeded. Always true
when status code is 200 and false
or undefined
otherwise.
A 64-byte (128 characters) hex string representing a login key derived using PBKDF#2.
If the request succeeded. Always true
when status code is 200 and false
or undefined
otherwise.
Retrieve a user's first-order connections in FIFO order.
user_id | [required] ID of the user. |
---|---|
after | [optional] Return only those results that occur after |
If the request succeeded. Always true
when status code is 200 and false
or undefined
otherwise.
The user's first-order connections as user_id
s.
Add a first-order connection between users user_id
and connection_id
.
The user's updatedAt
timestamp is also updated.
user_id | [required] ID of the first user. |
---|---|
connection_id | [required] ID of the second user. |
If the request succeeded. Always true
when status code is 200 and false
or undefined
otherwise.
Remove a first-order connection between users user_id
and connection_id
.
The user's updatedAt
timestamp is also updated.
user_id | [required] ID of the first user. |
---|---|
connection_id | [required] ID of the second user. |
If the request succeeded. Always true
when status code is 200 and false
or undefined
otherwise.
"5ec8adf06e38137ff2e58770"
"5ec8adf06e38e58770ff2137"
"My Little Opportunity"
9001
When this opportunity was first created in milliseconds since the unix epoch. Generated automatically by the server.
1579345900650
When this opportunity was last updated in milliseconds since the unix epoch. Generated automatically by the server.
1579345900650
The contents of a user's About section.
The contents of a user's Experience section.
The contents of a user's Education section.
The contents of a user's Volunteering section.
The contents of a user's Skills section.
When this entry began in milliseconds since the unix epoch.
When this entry ended in milliseconds since the unix epoch or null
if it is ongoing.
"5ec8adf06e38137ff2e58770"
A 16-byte (32 characters) hex string representing a salt corresponding to the login key.
"2d6843cfd2ad23906fe33a236ba842a5"
This user's unique username within the system. Must be lowercase alphanumeric (-
and _
are allowed).
"Oforce1"
This user's unique email address within the system.
"o@barackobama.com"
The type of this user. Possible values are: "inner"
, "staff"
, or "administrator"
.
The total number of views this user's profile has received.
1234
This user's sectional information (e.g. Experience, Education, etc).
When this user was first created in milliseconds since the unix epoch. Generated automatically by the server.
1579345900650
When this user was last updated in milliseconds since the unix epoch. Generated automatically by the server.
1579345900650