mirror of https://github.com/ghostfolio/ghostfolio
2 changed files with 139 additions and 0 deletions
@ -0,0 +1,137 @@ |
|||||
|
# Ghostfolio Public API Quickstart |
||||
|
|
||||
|
This guide shows a complete `curl` workflow for the Ghostfolio Public API: |
||||
|
|
||||
|
1. Exchange your security token for a Bearer token. |
||||
|
2. Verify connectivity with the health endpoint. |
||||
|
3. Import portfolio activities. |
||||
|
4. Retrieve a public portfolio snapshot. |
||||
|
|
||||
|
## Prerequisites |
||||
|
|
||||
|
1. A running Ghostfolio instance (for example `http://localhost:3333`). |
||||
|
2. A user account in Ghostfolio. |
||||
|
3. A security token from the _Access_ section in _My Ghostfolio_. |
||||
|
4. `curl` (and optionally `jq`) installed locally. |
||||
|
|
||||
|
## 1. Configure environment variables |
||||
|
|
||||
|
```bash |
||||
|
export GF_BASE_URL="http://localhost:3333" |
||||
|
export GF_SECURITY_TOKEN="<YOUR_SECURITY_TOKEN>" |
||||
|
``` |
||||
|
|
||||
|
## 2. Exchange security token for Bearer token |
||||
|
|
||||
|
Use the recommended `POST` endpoint: |
||||
|
|
||||
|
```bash |
||||
|
curl -sS \ |
||||
|
-X POST "$GF_BASE_URL/api/v1/auth/anonymous" \ |
||||
|
-H "Content-Type: application/json" \ |
||||
|
-d "{\"accessToken\":\"$GF_SECURITY_TOKEN\"}" |
||||
|
``` |
||||
|
|
||||
|
Example response: |
||||
|
|
||||
|
```json |
||||
|
{ |
||||
|
"authToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
Store it for later calls: |
||||
|
|
||||
|
```bash |
||||
|
export GF_BEARER_TOKEN="<PASTE_AUTH_TOKEN_FROM_RESPONSE>" |
||||
|
``` |
||||
|
|
||||
|
If you have `jq`, you can do it in one command: |
||||
|
|
||||
|
```bash |
||||
|
export GF_BEARER_TOKEN="$( |
||||
|
curl -sS \ |
||||
|
-X POST "$GF_BASE_URL/api/v1/auth/anonymous" \ |
||||
|
-H "Content-Type: application/json" \ |
||||
|
-d "{\"accessToken\":\"$GF_SECURITY_TOKEN\"}" | jq -r '.authToken' |
||||
|
)" |
||||
|
``` |
||||
|
|
||||
|
## 3. Verify the API is reachable |
||||
|
|
||||
|
The health endpoint does not require a Bearer token: |
||||
|
|
||||
|
```bash |
||||
|
curl -sS "$GF_BASE_URL/api/v1/health" |
||||
|
``` |
||||
|
|
||||
|
Expected response: |
||||
|
|
||||
|
```json |
||||
|
{ |
||||
|
"status": "OK" |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
## 4. Import activities |
||||
|
|
||||
|
Create a sample payload (`activities.json`): |
||||
|
|
||||
|
```json |
||||
|
{ |
||||
|
"activities": [ |
||||
|
{ |
||||
|
"currency": "USD", |
||||
|
"dataSource": "YAHOO", |
||||
|
"date": "2021-09-15T00:00:00.000Z", |
||||
|
"fee": 19, |
||||
|
"quantity": 5, |
||||
|
"symbol": "MSFT", |
||||
|
"type": "BUY", |
||||
|
"unitPrice": 298.58 |
||||
|
} |
||||
|
] |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
Submit the import request: |
||||
|
|
||||
|
```bash |
||||
|
curl -i \ |
||||
|
-X POST "$GF_BASE_URL/api/v1/import" \ |
||||
|
-H "Authorization: Bearer $GF_BEARER_TOKEN" \ |
||||
|
-H "Content-Type: application/json" \ |
||||
|
-d @activities.json |
||||
|
``` |
||||
|
|
||||
|
Expected status code: |
||||
|
|
||||
|
- `201 Created` |
||||
|
|
||||
|
Common validation error: |
||||
|
|
||||
|
- `400 Bad Request` with a message such as duplicate activity detection. |
||||
|
|
||||
|
## 5. Read a public portfolio snapshot |
||||
|
|
||||
|
1. In the app, create a public access entry in _My Ghostfolio_ -> _Access_. |
||||
|
2. Copy its `accessId`. |
||||
|
3. Request the public portfolio endpoint: |
||||
|
|
||||
|
```bash |
||||
|
export GF_ACCESS_ID="<YOUR_PUBLIC_ACCESS_ID>" |
||||
|
|
||||
|
curl -sS "$GF_BASE_URL/api/v1/public/$GF_ACCESS_ID/portfolio" |
||||
|
``` |
||||
|
|
||||
|
This endpoint is public and does not require a Bearer token. |
||||
|
|
||||
|
## 6. Troubleshooting |
||||
|
|
||||
|
- `401 Unauthorized` on `/api/v1/import`: |
||||
|
The Bearer token is missing, expired, or malformed. |
||||
|
- `400 Bad Request` on `/api/v1/import`: |
||||
|
Check required fields, date format (`ISO-8601`), and duplicates. |
||||
|
- Empty or unexpected portfolio response: |
||||
|
Verify the `accessId` belongs to a public access entry and is still active. |
||||
|
|
||||
Loading…
Reference in new issue