New York University
Using the NYU Archway
One NYU key reaches every AI vendor the university runs. All you ever decide is which service and which model - you name both as a single alias in model, and nothing else about the call changes.
Start here
What this is, and what the one key buys you.
The Archway is NYU's front door to commercial AI models. NYU holds the vendor accounts; you hold one NYU key beginning sk-nyu-, and that single key reaches every model NYU has enabled. You never see or handle a vendor key.
The gateway speaks the OpenAI API and the Anthropic API, so the SDK you already use keeps working - you change a base URL and a key, and nothing else. It records who called which model and how many tokens, and it enforces a token allowance per person, per vendor.
Same for every vendor
One host, one key, one set of endpoints, whether you are calling Claude, GPT, Gemini or Perplexity. There is no second URL to learn and no per-vendor key to manage.
Your text is never stored
Prompts and responses are not written to the database - not to usage records, not to logs. The gateway keeps who called what, when, and how many tokens. Nothing else.
Step 1 · Get a key
Start in seconds with a demo key, or ask for a standing one.
The quickest way in is a demo key.
Sign in, open Get access and press the button. Nobody approves it. It stops on whichever comes first: 3 days, or $5.00 of vendor spend. Every demo key also ends at the start of the week whatever day it was claimed, so the next one is available on 28 Sep 2026, 00:00 UTC — one per person per week.
A demo key reaches
29 of the models below —
claude-haiku-4-5 claude-opus-5 claude-sonnet-5 gemini-3-pro-image gemini-3.1-flash-image gemini-3.1-flash-lite and 23 more.
The rest cost too much to be worth a $5.00 trial. GET /v1/models
on your key lists exactly what it can call; anything else answers 403.
Two differences from a standing key, both because the spend ceiling is checked
before each call rather than noticed afterwards. A request that names no
max_tokens is given 4,096 rather than the model's
maximum; and near the end of the budget a call can be refused with 402 while a
few cents remain, because the check has to assume the longest answer your request allows.
Lowering max_tokens usually lets it through, and the error says so.
- Sign in at the portal.
- Open Get access and say what you need the key for, and which allowance tier you want. You can have one open request at a time. A demo key needs none of this, and you can use one while you wait.
- Wait for approval. The decision shows on Get access, and an approved request issues your key in the same moment.
- Open My keys and press Show me the key. Nobody has seen the secret — not the person who approved you, not the gateway — and you can read it once. Have somewhere to paste it ready: if it is lost you have to request another key. You may hold up to 10 keys at a time.
A key is shown exactly once.
The gateway stores a peppered hash and the first 15 characters, never the secret, so nobody - including the Archway team - can read it back to you. That holds for a key the team issued you as much as for one you made yourself: theirs is parked for you to open in your own portal, unread by anyone, and opening it spends the only view there is. Lost it? Revoke it and ask for another. Revoke straight away if a key reaches a repository, a shared notebook or a chat message.
Two gates govern signing in, and both must pass: the account has to exist, and your address has to be on this deployment's sign-in allowlist. A refused address is deliberately indistinguishable from a wrong password - if you are certain of your password, ask the team whether you are on the list.
Step 2 · Point your client at it
Two settings. Everything else in your code stays as it is.
- Base URL
- https://srv1990842.hstgr.cloud/v1 for OpenAI-shaped clients
- Base URL
- https://srv1990842.hstgr.cloud for the Anthropic SDK, which appends /v1 itself
- Your key
- Authorization: Bearer sk-nyu-… or x-api-key: sk-nyu-… - both work on every endpoint. If you send both, Authorization wins.
| Endpoint | Method | What it is for |
|---|---|---|
| /v1/chat/completions | POST | Chat, streaming or not, in the OpenAI request shape |
| /v1/messages | POST | The same thing in the Anthropic request shape |
| /v1/embeddings | POST | Embeddings, OpenAI shape |
| /v1/models | GET | Exactly the models your key may call |
| /v1/providers | GET | The same, grouped by service, with what is left of each allowance |
| /v1/key | GET | What your key is and everything it unlocks, in one request |
Those endpoints are the whole surface, for every vendor. The dialect you speak and the vendor you reach are independent: an Anthropic-shaped request naming a GPT or Gemini alias is translated for you, and an OpenAI-shaped request naming a Claude alias is too. Use whichever SDK your code already has.
Step 3 · Your first call
Start with /v1/models: it answers "what can I actually call" in one request and costs no tokens.
curl https://srv1990842.hstgr.cloud/v1/models \
-H "Authorization: Bearer $NYU_API_KEY"
Each entry carries the alias to put in model, plus its service, context window and maximum output. Use one of those aliases - a vendor's own model string is not an alias.
GET /v1/providers answers the same question the other way round: one entry per service your key is cleared for, each carrying the aliases it may send, how much of that service's allowance is left, and whether a real vendor is behind it. A service your key cannot use is simply absent. GET /v1/key returns that list plus your key's own scope and expiry, which is the one call to make when a program needs to know everything at once. Neither costs a token, and neither says anything about the account the key belongs to.
export NYU_API_KEY="sk-nyu-…" # the key you were shown once
curl https://srv1990842.hstgr.cloud/v1/chat/completions \
-H "Authorization: Bearer $NYU_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "claude-fable-5-1",
"messages": [{"role": "user", "content": "Hello"}],
"max_tokens": 256
}'
import os
from openai import OpenAI
client = OpenAI(
api_key=os.environ["NYU_API_KEY"],
base_url="https://srv1990842.hstgr.cloud/v1", # the NYU gateway, not the vendor
)
response = client.chat.completions.create(
model="claude-fable-5-1",
messages=[{"role": "user", "content": "Hello"}],
max_tokens=256,
)
print(response.choices[0].message.content)
print(response.usage) # the token counts your allowance is measured in
import os
from anthropic import Anthropic
client = Anthropic(
api_key=os.environ["NYU_API_KEY"],
base_url="https://srv1990842.hstgr.cloud", # no /v1 - the SDK appends it
)
message = client.messages.create(
model="claude-fable-5-1",
max_tokens=256,
messages=[{"role": "user", "content": "Hello"}],
)
print(message.content[0].text)
Always send max_tokens.
It is what the gateway reserves against your allowance. With no value it assumes 4,096 tokens of headroom, which can refuse a request you had room for. Streaming works on both surfaces with "stream": true.
On a thinking model, max_tokens also pays for the thinking.
Reasoning and the visible answer come out of one budget, so a cap that is generous elsewhere can return a truncated answer - or an empty one, with finish_reason: "length" - with nothing wrong with your request. The gateway therefore pins a modest effort for Google Gemini (low). Send reasoning_effort yourself to override it - minimal, low, medium or high - and raise max_tokens along with it. The Pro models have a floor and will not go as low as minimal.
GET /v1/models carries default_reasoning_effort
on exactly the models where the parameter does something. A vendor without the control
ignores the field rather than failing, so it is safe to leave set across a model switch.
Services and models
The alias names both. Naming the model is how you name the service.
| Service | Example aliases |
|---|---|
| Anthropic | claude-fable-5-1 · claude-haiku-4-5 · claude-opus-5 |
| OpenAI | gpt-5.6 · gpt-5.6-luna · gpt-5.6-sol |
| Google Gemini | gemini-2.5-flash · gemini-2.5-pro · gemini-3-flash-preview |
| Perplexity | sonar · sonar-pro · sonar-reasoning-pro |
Examples, not the catalogue. GET /v1/models returns exactly what your key may call right now, and is the list to trust.
Knowing where you stand
Tokens, per service, per period. Every response tells you your standing.
Your allowance is counted in tokens, per service, per period - spending your Anthropic allowance does not touch your OpenAI one. These headers come back on every response: on success, on denial and on error.
| Header | Meaning |
|---|---|
| X-NYU-Tokens-Limit | Your limit this period, or unlimited |
| X-NYU-Tokens-Used | Tokens already counted against it |
| X-NYU-Tokens-Remaining | What is left, or unlimited |
| X-NYU-Quota-Window | The period: monthly, weekly or daily |
| X-NYU-Quota-Resets | When the counter rolls over, UTC |
| X-NYU-Request-Id | Quote this when you ask for help |
| X-NYU-Provider | Which service actually served the call |
| X-NYU-Model | The alias it resolved to |
Why a call can be refused while tokens appear to remain.
The gateway reserves tokens before calling the vendor - your prompt estimate plus room for the answer - and swaps the reservation for the vendor's real count afterwards. A large request can therefore be refused while X-NYU-Tokens-Remaining still shows headroom: the reservation did not fit, even though the answer might have. A smaller max_tokens makes a smaller reservation.
Nothing is zeroed on a schedule. A new period is simply a new counter, so your allowance returns at the moment shown in X-NYU-Quota-Resets. Enforcement here is hard: at the limit, requests are refused.
Burst limits are separate from your allowance and bound abuse rather than cost: 600 requests per minute per key and 1,200 per minute per person. A response carrying X-NYU-Mock: true never reached a vendor - that service has no live credential here, so the content is a deterministic placeholder even though the token accounting is real.
When something goes wrong
Always the OpenAI error envelope, whichever endpoint you called, so your SDK's own error handling works. Read type.
| Status | Type | What to do |
|---|---|---|
| 401 | authentication_error | The key is wrong, revoked or expired, or your account is inactive. The gateway will not say which - that would make it an oracle for probing keys. Check for a truncated paste first. |
| 403 | permission_denied | Your key is real but scoped away from that model or service. GET /v1/providers lists every service it may use, and /v1/models every model. |
| 404 | model_not_found | The alias does not exist or is disabled. Take one from /v1/models. |
| 429 | quota_exceeded | Out of tokens for that service this period. Read X-NYU-Quota-Resets, or ask for a higher tier. |
| 429 | rate_limit_exceeded | A burst limit, not your allowance. The message says how many seconds to wait. |
| 502 504 | upstream_unavailable upstream_timeout |
The vendor failed or was slow, not you. Retry with backoff. |
| 503 | service_frozen | The Archway team has paused all traffic during an incident. Retry later. |
Quote the request id.
Every response carries X-NYU-Request-Id. It is how the team finds your exact call - and the only way they can, because prompts and responses are never stored. Include the id, the alias you called and the time in UTC.
API reference
Every route, field and schema, generated from the code.
This page is the guide. For the full contract - every endpoint, every request and response field, including the admin and portal JSON APIs - the machine-readable schema is served at /openapi.json, and the repository carries a generated docs/API_REFERENCE.md alongside it.