Skip to main content

justETF Mobile Client

An unofficial client for justETF's mobile API

Go 1.22 or newer Python 3.10 or newer Native ABI version 1 Unofficial API

Go core library with Python bindings via ctypes.

Quick start | API surface | Python | Go | Build | Testing | C ABI

Quick Start

Clone the repository:

git clone https://github.com/wavepacket0/justetf-mobile-client.git
cd justetf-mobile-client
# Makes a live public profile request for the sample ISIN IE000VAHT5T0
go run ./examples/go profile

To build from source, follow Build.

See examples/ for additional Go and Python examples and Test for testing. For authenticated endpoints, you need to provide an account.

API Surface

Area Included
Public REST profiles, discovery, and financial data
Account API authenticated endpoints and profile updates
Go Native client with caller contexts and typed request options
Python Standard-library ctypes, decoded JSON, and exact raw-response access
Native ABI Versioned C interface with explicit buffer ownership

Shared HTTP execution

Endpoint methods define the route and request data, then delegate HTTP work to a shared Go executor. It builds and sends requests, applies common mobile API headers (client identity, language, API key, and request ID), and handles responses and errors.

Authentication is endpoint-specific: public requests remain unauthenticated even after login, while protected requests use the session managed by the Go client. Login and token refresh stay internal, session tokens are not exposed through the Python interface.

Build

The Python binding loads the Go client as a shared library. Build it for the same OS and architecture as the Python interpreter. Local builds require Go 1.22+, Python 3.10+, and a C compiler supported by cgo (MinGW-w64 GCC on Windows, Xcode Command Line Tools on macOS, or GCC/Clang on Linux).

Native library

# Windows
New-Item -ItemType Directory -Force bin | Out-Null
go build -trimpath -buildmode=c-shared -o bin/justetf-native.dll ./cmd/justetf-native
# Linux
mkdir -p bin
go build -trimpath -buildmode=c-shared -o bin/libjustetf-native.so ./cmd/justetf-native
# macOS
mkdir -p bin
go build -trimpath -buildmode=c-shared -o bin/libjustetf-native.dylib ./cmd/justetf-native

Python wheel

Build and smoke-test a wheel from the repository root. Wheel builds require cibuildwheel. Linux builds also require Docker or Podman.

python -m pip install --upgrade cibuildwheel
cp LICENSE python/LICENSE   # CI does this so the wheel bundles the license
python -m cibuildwheel --config-file python/pyproject.toml --output-dir wheelhouse python

Each wheel targets the current OS and architecture, bundles the matching shared library, and runs an isolated mock-API smoke test.

Test

Install test-only dependencies, then run the offline suite:

python -m pip install -r test/requirements.txt
python test/run.py --mode offline

Run the optional handle-lookup benchmark with:

go test ./cmd/justetf-native -run '^$' -bench BenchmarkSessionLookup -benchmem

Python tests build the Go shared library in bin, load it with ctypes, and send requests to local mock servers. By default, tests use mock servers and synthetic account credentials, never the real API. The handle benchmark measures registry lookup overhead, not network or whole-client speed.

Go unit tests stay beside their packages. Root test/ owns a shared Python mock server and synthetic catalogue for both clients. Choose a test tier with JUSTETF_TEST_MODE:

Tier Target and behavior
offline (default) Local mock server and synthetic credentials; no real API calls. Exercises reads, writes, and failure cases.
online-readonly Online API, performs login/refresh and reads without creating or deleting account records.
online Online API, also creates scoped test records and cleans them up. Use a dedicated test account.

Copy .env.example to .env to configure the runner. Leave credentials blank for offline, both online tiers require an account email and password:

JUSTETF_TEST_MODE=offline
JUSTETF_TEST_EMAIL=
JUSTETF_TEST_PASSWORD=

The .env file is Git-ignored and read only by the test runner. --mode overrides the value in .env. See the test guide for run commands and safety details.

Python usage

Runnable Go and Python code for profile lookup, search, financial data, and account reads is in examples/. The tables below list every method.

Search and discovery

Go Method Python Method / Native Operation Python Options
GetUniverseCountries get_universe_countries None
GetAccountAccess get_account_access Optional country string
GetSearchMetadata get_search_metadata SearchMetadataOptions
SearchETFs search_etfs SearchOptions
SearchISINs search_isins SearchISINsOptions
GetCards get_cards CardsOptions
CompareETFs compare_etfs ComparisonOptions
SearchHoldings search_holdings HoldingSearchOptions

Every Python method has a _json variant returning owned bytes. Option dataclasses are frozen, keyword-only, and optional, omitted values are resolved in Go, not by a separate Python implementation. Go methods take their typed options as values (use the zero value for defaults). Pointer-valued Go Limit fields distinguish omission from an invalid explicit zero. Responses remain raw JSON in Go and dynamically decoded values in Python, no unverified response schema is enforced.

Defaults, overridable per request:

  • Country/currency come from the client, universe type is PRIVATE, period end is YESTERDAY, period is Y1.
  • Search: fund size descending, limit 20, page 0. Cards: BEST, limit 30. Search-ISINs: LARGEST. Holding search: name ascending, limit 15.
  • Pages and offsets must be nonnegative, limits positive. No upper limit or closed enum set is imposed.
  • Search filters are opaque identifiers from search metadata. Omitted lists serialize as [], never null.
  • The API's returnPeriod (search) versus returnsPeriod (cards) spelling is preserved.

These methods reproduce observed unauthenticated requests.

Financial REST data

Go Method Python Method / Native Operation Options
GetQuote get_quote CurrencyOptions
GetPerformanceChart get_performance_chart ChartOptions
GetReturns get_returns PeriodEndOptions
GetRisks get_risks PeriodEndOptions
GetCountryExposure get_country_exposure ExposureOptions
GetSectorExposure get_sector_exposure ExposureOptions
GetHoldingExposure get_holding_exposure ExposureOptions
GetHoldingDetails get_holding_details MarketOptions
GetHoldingETFs get_holding_etfs HoldingETFsOptions
GetBrokers get_brokers UniverseOptions
GetMarketOverview get_market_overview MarketOverviewOptions
GetIndexPerformance get_index_performance IndexPerformanceOptions
GetRecommendedFilters get_recommended_filters RecommendedFiltersOptions
GetSavingsPlans get_savings_plans SavingsPlansOptions
GetOrderFees get_order_fees OrderFeesOptions

Instrument methods take an ISIN before options. Brokers, overview, index performance, and recommended filters take options only. Every Python method has a _json variant. get_profile also accepts MarketOptions. Go retains GetProfile and adds GetProfileWithOptions, preserving the legacy C entry.

Charts:

  • Dates use YYYY-MM-DD: reversed or malformed ranges are rejected.
  • Omitted dates send bare dateFrom/dateTo keys: explicit empty strings send dateFrom=/dateTo=.
  • Period defaults to Y1: period="" sends a bare period for custom ranges. There is no implicit switch between period and custom-range mode.
  • Reduction and dividends default to true, values type to RELATIVE_CHANGE. Explicit false values are preserved.
  • Unavailable charts raise APIError with status 404, never an empty success.

Other defaults (all overridable through options):

  • Exposure: top 4 for countries/sectors, top 10 for holdings, scale 4, threshold "0.01".
  • Holding ETFs: top 15, scale 2, page 0.
  • Overview: D1, yesterday, home-screen-only false.
  • Index performance: TOP/RETURN_Y1/ALL, limit 50.
  • Savings plans: amount "100", RATING. Order fees: amount "100", BEST.

Thresholds and monetary request amounts are decimal strings, never floats. Plain nonnegative decimals are accepted, monetary amounts must be positive. Exponent notation, negative amounts, NaN, and infinity are rejected. Counts must be positive, scales/pages nonnegative, explicit zero scale is preserved. Raw responses retain nulls, unknown fields, and exact numeric tokens. Holding supplementary data uses the authenticated account-read API below.

Defaults are Italian, IT, and EUR. Timeout is a positive number of seconds, at most 300, rounded up to milliseconds. It covers the entire Go HTTP operation including reading the body.

Decoded results use Python's standard JSON numeric behavior. For exact decimal processing, use raw JSON with json.loads(raw_json, parse_float=Decimal) from decimal. The Go/native/raw-bytes path preserves the original numeric tokens.

Errors are ValueError for invalid arguments/configuration, APIError with status_code for HTTP failures, TransportError for I/O/timeouts/invalid upstream JSON, and ClientClosedError for closed clients. Other native failures raise ClientError. Missing/changed sessions raise AuthenticationError, failed upstream login/refresh HTTP responses still raise APIError with their status.

Authentication

from getpass import getpass
from justetf_mobile_client import Client

# For a local build. With an installed wheel, use Client() and omit the path.
with Client("bin/justetf-native.dll") as client:
    client.login(input("Email: "), getpass("Password: "))
    print(client.is_authenticated())
    client.refresh()
    client.logout()

Go provides Login(ctx, email, password) error, Refresh(ctx) error, Logout(), IsAuthenticated() bool, and Close(). Python login/refresh/logout return None. The status method reports local token presence, not remote validity or expiry.

Login reproduces GET /v1/auth/tokens with Basic authorization. Refresh uses GET /v1/auth/refreshed-tokens with the refresh token as Bearer authorization. Both send {} and the standard mobile headers. Tokens stay in the Go client and are not persisted or returned through the ABI. Passwords are not stored on the client. Login starts a new session and clears the old one before its request, a failed login leaves the client unauthenticated. Logout/close clear stored token references and invalidate pending authentication results. Managed-runtime memory is not guaranteed to be securely overwritten.

When an authenticated read gets a 401, the client uses its refresh token to get a new access token and retries the read once. If several reads need this at once, only one refresh request is sent. Cancelling one Go request does not cancel that refresh for other reads: logout, close, or the client timeout does.

After an automatic refresh fails, the client does not keep retrying it on every request. Call refresh() to try again. If justETF rejects the refresh token with 401 or 403, log in again.

Authenticated Account Reads

from getpass import getpass
from justetf_mobile_client import Client, PortfolioListOptions, TradeQuoteOptions

# For a local build. With an installed wheel, use Client() and omit the path.
with Client("bin/justetf-native.dll") as client:
    client.login(input("Email: "), getpass("Password: "))
    watchlists = client.get_watchlists()
    portfolios = client.get_portfolios(PortfolioListOptions(limit=10, page=0))
    quote = client.get_trade_quote("IE00B5BMR087", TradeQuoteOptions(date="2026-10-14"))

All listed methods require login() and read from that account; they do not change account data. Eligible reads can refresh the session and retry once after a 401. IDs are passed as strings. Python returns decoded JSON by default, use a method's _json variant when you need the raw response bytes.

Go Method Python Method / Native Operation Arguments After Context / Client
GetPersonalData get_personal_data Optional country string (Go: empty string for default)
GetWatchlists get_watchlists WatchlistOptions
GetSavedFilters get_saved_filters None
GetPriceAlerts get_price_alerts None
GetETFPriceAlerts get_etf_price_alerts ISIN
GetPriceAlert get_price_alert ISIN, alert ID
GetPortfolios get_portfolios PortfolioListOptions
GetPortfolio get_portfolio Portfolio ID, PortfolioOptions
GetPortfolioComposition get_portfolio_composition Portfolio ID, PortfolioCompositionOptions
CanCreatePortfolio can_create_portfolio None; reads eligibility, does not create
GetTradeQuote get_trade_quote ISIN, TradeQuoteOptions with required date
GetPortfolioTransactions get_portfolio_transactions Portfolio ID, PageOptions
GetPortfolioAllocation get_portfolio_allocation Portfolio ID, AllocationOptions
GetPortfolioPerformanceChart get_portfolio_performance_chart Portfolio ID, PortfolioChartOptions
GetHoldingSupplementaryData get_holding_supplementary_data ISIN

Account Updates

These operations change justETF account data, a portfolio transaction is only a tracker entry, not a broker order. Live updates require explicit opt-in and should use a dedicated test account. Routine tests use local mocks.

Go Method Python Method / Native Operation Parameters Result
CreatePriceAlert create_price_alert ISIN, PriceAlertRequest JSON
DeletePriceAlert delete_price_alert ISIN, alert ID Void
CreateSavedFilter create_saved_filter SavedFilterRequest Void
DeleteSavedFilter delete_saved_filter Saved-filter ID Void
CreatePortfolio create_portfolio CreatePortfolioRequest JSON
DeletePortfolio delete_portfolio Portfolio ID Void
AddShareTransaction add_share_transaction Portfolio ID, ShareTransactionRequest JSON

Pass monetary values as decimal strings in Python and json.Number in Go to preserve precision, the client does not infer missing transaction values. Updates are never retried automatically. If a timeout or transport error leaves the result uncertain, check the account before trying again. Only the operations listed above are supported, other account changes are not implemented.

Concurrency and lifetime

Clients support concurrent calls and reuse HTTP connections. Use with or call close() when finished. Closing cancels active requests and clears the session, but does not wait for every call to return.

C ABI v1

The build produces a C header beside the shared library. That header defines ABI version 1, the C types, and the calling conventions. The main entry points create and close client handles, dispatch supported operations, report the ABI version, and free returned buffers. The legacy justetf_profile entry point remains available.

Clients are referenced by opaque handles. Calls pass UTF-8 input bytes and return a JustETFBuffer containing output bytes. Input memory remains caller-owned and must stay valid for the call. Free each returned buffer exactly once with justetf_buffer_free. Output is not guaranteed to be NUL-terminated, and Go pointers do not cross the ABI.

justetf_call accepts only registered operations and JSON parameters. It is not an arbitrary HTTP or URL interface. Check the returned status for success, invalid input, API errors, transport errors, closed handles, internal errors, or authentication and session errors. Use a native library matching the wrapper version and rebuild it when required exports are missing.

Go usage

The root Go package is justetf. Import it from github.com/wavepacket0/justetf-mobile-client.

client, err := justetf.NewClient(justetf.Config{
        Timeout: 15 * time.Second,
})
if err != nil {
        return err
}
defer client.Close()
profile, err := client.GetProfile(ctx, "IE000VAHT5T0")

Direct Go consumers can supply per-request contexts for cancellation. The native binding exposes a per-client timeout and client-wide cancellation through Close(), but not per-request cancellation handles. CloseIdleConnections() only manages the connection pool. Use Close() to cancel work, clear authentication, and end the client's lifecycle.

Protocol defaults and implemented endpoint paths live in constants.go. Add endpoint constants as features are implemented rather than copying the full research catalog into runtime code. Country, currency, and language remain per-client configuration. Tests retain independent expected protocol literals.

Disclaimer

This is an independent, unofficial project and is not affiliated with, endorsed by, sponsored by, or supported by justETF. It uses justETF's mobile API, which may change or become unavailable without notice. The justETF name, logo, and other branding remain the property of justETF or their respective rights holders.

Metadata

Release files for justetf-mobile-client 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Built distributions (wheels)

Table of built distributions (wheels) for justetf-mobile-client 0.1.0
File Actions
justetf_mobile_client-0.1.0-py3-none-win_amd64.whl Python 3 none Windows x86-64 Details for justetf_mobile_client-0.1.0-py3-none-win_amd64.whl
justetf_mobile_client-0.1.0-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl Python 3 none Linux glibc 2.17+ ARM64, Linux glibc 2.28+ ARM64 Details for justetf_mobile_client-0.1.0-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl
justetf_mobile_client-0.1.0-py3-none-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl Python 3 none Linux glibc 2.5+ x86-64, Linux glibc 2.28+ x86-64 Details for justetf_mobile_client-0.1.0-py3-none-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl
justetf_mobile_client-0.1.0-py3-none-macosx_11_0_arm64.whl Python 3 none macOS 11.0+ ARM64 Details for justetf_mobile_client-0.1.0-py3-none-macosx_11_0_arm64.whl
justetf_mobile_client-0.1.0-py3-none-macosx_10_9_x86_64.whl Python 3 none macOS 10.9+ x86-64 Details for justetf_mobile_client-0.1.0-py3-none-macosx_10_9_x86_64.whl

Total release size: 22.7 MB

Release files / justetf_mobile_client-0.1.0-py3-none-win_amd64.whl

Download URL justetf_mobile_client-0.1.0-py3-none-win_amd64.whl
Size 5.7 MB
Tags Python 3 Windows x86-64
SHA-256 checksum
How to use checksums
788257ef6adf99a112848879922278ee044d1f9c6ab94f0d698483e2f1e6c0de
BLAKE2b-256 checksum
How to use checksums
2923bd65f59266ffe5e5bbad858b7dd020decee4cd985ad538e3a9efe4f1b2ba
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 2, 2026.

Transparency log

Release files / justetf_mobile_client-0.1.0-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl

Download URL justetf_mobile_client-0.1.0-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl
Size 5.2 MB
Tags Linux glibc 2.17+ ARM64 Linux glibc 2.28+ ARM64 Python 3
SHA-256 checksum
How to use checksums
5629113f9bfbb0f68924a30884962da58c7a154a1ca00bb5838b35ba87bc27a0
BLAKE2b-256 checksum
How to use checksums
7a65a76e709ebcae699d891070f07a332f82444d319f060b84ad0a7b2c4121bb
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 2, 2026.

Transparency log

Release files / justetf_mobile_client-0.1.0-py3-none-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl

Download URL justetf_mobile_client-0.1.0-py3-none-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl
Size 5.8 MB
Tags Linux glibc 2.28+ x86-64 Linux glibc 2.5+ x86-64 Python 3
SHA-256 checksum
How to use checksums
21908a29d9ad7c784b175df7344bff9c551e0aeeeecde84b0261dfd95d3db795
BLAKE2b-256 checksum
How to use checksums
18308499993d9b34d3fbd2d485b38431958ac704e8a6809a432f789285e209b2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 2, 2026.

Transparency log

Release files / justetf_mobile_client-0.1.0-py3-none-macosx_11_0_arm64.whl

Download URL justetf_mobile_client-0.1.0-py3-none-macosx_11_0_arm64.whl
Size 2.9 MB
Tags Python 3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
9f95cc5619f6d1a994fde2a1c90146871fe764269d4b3d29bce51b0501e59911
BLAKE2b-256 checksum
How to use checksums
06e5ed5b0a43c31974eea85b768ae2d4bb989feed43b99b6ed9e923f14f80181
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 2, 2026.

Transparency log

Release files / justetf_mobile_client-0.1.0-py3-none-macosx_10_9_x86_64.whl

Download URL justetf_mobile_client-0.1.0-py3-none-macosx_10_9_x86_64.whl
Size 3.2 MB
Tags Python 3 macOS 10.9+ x86-64
SHA-256 checksum
How to use checksums
29c8268946c3eb356fedfb452ca45a24fa79143d85d85709809389b6a096d19d
BLAKE2b-256 checksum
How to use checksums
09c139a41c8e0db19c18267d48899b1ae24409b988f84ac816872d2849f7494a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 2, 2026.

Transparency log

Security for justetf-mobile-client 0.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 5 files in this release have been uploaded with provenance metadata.

Select from 5 files

Attestation for justetf_mobile_client-0.1.0-py3-none-win_amd64.whl

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 2, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.0 This release

5 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page