Gemini · generateContent
Use Gemini API through Tikway: generateContent and authentication
Last updated:
Gemini’s native protocol places the model in the URL and uses contents[].parts[], unlike OpenAI’s model and messages structure. This guide sends a minimal generateContent request, saves results, reads candidate text and explains multi-turn messages and streaming action paths.
Authenticate using a Tikway key in x-goog-api-key. Calls are billed by usage; check the catalog for current protocol support, modalities and prices first.
1. Put the model in the correct URL
The protocol documentation shows:
POST https://api.tikway.ai/v1beta/models/gemini-2.5-flash:generateContent
gemini-2.5-flash is an example ID, not a promise of availability at all times. Confirm the current call ID. If a resource name already includes models/, avoid appending that segment twice. Use the documented call example for namespaced IDs or aliases.
2. Save a request and call generateContent
Use Bash, curl 7.76 or newer and Python 3.10 or newer for response reading, without third-party Python packages. Set the key in the same terminal:
export TIKWAY_API_KEY='YOUR_TIKWAY_API_KEY'
Save as 04-gemini-request.json. The downloadable JSON has the same structure and a Chinese prompt:
{
"contents": [
{
"role": "user",
"parts": [
{
"text": "Explain the role of an API gateway in an application in three sentences."
}
]
}
]
}
Run in the file’s directory. This is a real model request; the response body and headers are saved locally, with costs governed by your account and the current model rules.
: "${TIKWAY_API_KEY:?Set the Tikway API Key environment variable first}"
curl --fail-with-body --silent --show-error \
--connect-timeout 10 --max-time 60 \
--request POST \
'https://api.tikway.ai/v1beta/models/gemini-2.5-flash:generateContent' \
--header 'Content-Type: application/json' \
--header "x-goog-api-key: ${TIKWAY_API_KEY}" \
--data-binary '@04-gemini-request.json' \
--dump-header 'gemini-response.headers' \
--output 'gemini-response.json'
The gateway reads the model from the URL, so this minimal request needs no root model. If your application also sends that field, keep it consistent with the path. --fail-with-body preserves an HTTP error response and exits nonzero. Network failures may produce empty or partial files; check the request result before parsing.
3. Read ordinary text from candidates
This fabricated example illustrates response structure:
{
"candidates": [
{
"content": { "role": "model", "parts": [{ "text": "Example answer." }] },
"finishReason": "STOP"
}
],
"usageMetadata": { "promptTokenCount": 100, "candidatesTokenCount": 40 }
}
Read the first candidate’s content.parts and concatenate ordinary text fields. Tool calls and parts marked thought should not be treated as the final answer. If candidates are missing, inspect feedback rather than blindly indexing [0].
Save as 04-read-gemini-response.py. The downloadable reader has the same logic and Chinese diagnostics. It reads only local JSON:
"""Read a saved Gemini generateContent JSON response locally; no API calls."""
import json
from pathlib import Path
import sys
def extract_text(response):
if not isinstance(response, dict):
raise ValueError("Response must be a JSON object.")
if response.get("error"):
raise ValueError("Response contains an error; check the HTTP status and response headers.")
candidates = response.get("candidates")
if not isinstance(candidates, list) or not candidates:
feedback = response.get("promptFeedback", {})
reason = feedback.get("blockReason", "not provided") if isinstance(feedback, dict) else "not provided"
raise ValueError(f"No candidates; promptFeedback.blockReason={reason}")
candidate = candidates[0]
if not isinstance(candidate, dict):
raise ValueError("First candidate is not a JSON object.")
content = candidate.get("content", {})
parts = content.get("parts", []) if isinstance(content, dict) else []
if not isinstance(parts, list):
raise ValueError("content.parts is not an array.")
text = "".join(
part["text"] for part in parts
if isinstance(part, dict) and part.get("thought") is not True
and isinstance(part.get("text"), str)
)
if not text:
raise ValueError(f"No ordinary text; finishReason={candidate.get('finishReason', 'unknown')}")
return text
def main():
path = Path(sys.argv[1] if len(sys.argv) > 1 else "gemini-response.json")
response = json.loads(path.read_text(encoding="utf-8"))
print(extract_text(response))
reason = response["candidates"][0].get("finishReason")
if reason == "MAX_TOKENS":
print("Note: output limit reached; the answer may be truncated.", file=sys.stderr)
if __name__ == "__main__":
try:
main()
except (OSError, ValueError, UnicodeError) as exc:
print(f"Read failed: {exc}", file=sys.stderr)
sys.exit(1)
Run:
python 04-read-gemini-response.py gemini-response.json
MAX_TOKENS may indicate an output limit. For missing candidates, the script reports promptFeedback.blockReason, if present. These checks distinguish restricted content, non-text results and invalid file structure.
4. Add conversation history and system instructions
Gemini uses model for assistant turns and user for user turns. Put system instructions in root systemInstruction.parts. Merge these fields into a full request; the assistant text is a fabricated format example:
{
"systemInstruction": { "parts": [{ "text": "Be concise and use English." }] },
"contents": [
{ "role": "user", "parts": [{ "text": "What is an API?" }] },
{ "role": "model", "parts": [{ "text": "An API is an interface for exchanging data or calling functions between programs." }] },
{ "role": "user", "parts": [{ "text": "Illustrate this with a weather lookup." }] }
]
}
Generation parameters belong in generationConfig. Use maxOutputTokens when the model supports it; it differs from Anthropic’s max_tokens and OpenAI’s max_completion_tokens. Check image, tool and thinking parameters separately for the selected model.
5. Change the action path for streaming
For a model supporting streaming, use :streamGenerateContent. Add alt=sse for SSE events; curl’s --no-buffer reduces client output buffering:
: "${TIKWAY_API_KEY:?Set the Tikway API Key environment variable first}"
curl --fail-with-body --silent --show-error --no-buffer \
--connect-timeout 10 --max-time 90 \
--request POST \
'https://api.tikway.ai/v1beta/models/gemini-2.5-flash:streamGenerateContent?alt=sse' \
--header 'Content-Type: application/json' \
--header "x-goog-api-key: ${TIKWAY_API_KEY}" \
--data-binary '@04-gemini-request.json'
This makes another real call with the same input file. Adding only stream: true to generateContent does not select the streaming action. Parse each event using Gemini’s structure, rather than Chat Completions’ choices[].delta.content or [DONE] completion rule. The ordinary JSON reader above cannot parse this event stream.
Troubleshoot common errors
| Symptom | First checks |
|---|---|
| Authentication failure | Tikway key in the current process and x-goog-api-key. |
| Unknown action or model | /v1beta/models/, exact ID and action after the colon. |
| Body/path model conflict | Omit an unnecessary root model or make it consistent. |
| Invalid input | Nonempty contents and parts, with text in parts[].text. |
| No candidates | promptFeedback and errors; candidate zero is not guaranteed. |
429 / RESOURCE_EXHAUSTED |
Message, account state and request frequency; rate limiting is not the only possible cause. |
| Ineffective output limit | generationConfig.maxOutputTokens and model parameter support. |
| Partial or no ordinary text | Finish reason, output limit, tools and restrictions. |
Keep x-request-id, model ID and action for troubleshooting. The operator states that it supplies the requested model; metadata and generated self-descriptions alone do not prove model identity.
Continue with streaming troubleshooting, authentication checks and token costs. Protocol references currently in Chinese: Gemini · Authentication · Error codes.