Live Tennis API DocsGet a free key
Explore the documentation

Live Tennis API · docs · version 1.13.60

Tennis players, tournaments and rankings API

How do you look up a player, a tournament or a ranking? GET /players searches by name and GET /players/{playerId} returns one player's bio, current ranking and cached statistics — both FREE. GET /tournaments is the catalogue that Match.tournament_id joins against, so a match can be resolved to its event without a second vendor. GET /rankings (PRO) serves rank-ordered listings and per-player as-of records, including Elo.

7 endpoints on this page. Base URL https://api.livetennisapi.com/api/public/v1; authenticate with the X-API-Key header. Plans involved: FREE, PRO. A free key needs no card.

The endpoints

Each one below lists its plan tier, every parameter, the response shape and an example. They are the same entries as in the full reference, which carries all 48 operations of the API on one page.

GET /players/{playerId}/stoppages

One player's in-match stoppages and did-not-finish outcomes, newest first over a window (PRO)

Plan required: PRO · operationId: listPlayerStoppages

Added 2026-09-16. Medical timeouts and trainer calls on the player's matches (from the stoppage family of GET /matches/{matchId}/events), plus retirements and walkovers where THIS player is the non-winner, merged and sorted by at descending. Window: since/until (ISO date or UTC instant; default the last 180 days; at most 366 days, else 400 window_too_long). kind filters the seven row kinds (default medical_timeout,trainer_called,retirement,walkover); before pages by meta.next_cursor. meta.latest_medical_timeout and previous_medical_timeout are the two newest medical timeouts in the window whatever the page or the kind filter. meta.record_starts states how far back each family goes: stoppage rows exist from 2026-09-12 only; outcome rows from the oldest match with a stated winner. In-match stoppages and match outcomes only — no off-court injury record exists here. Same PRO capability as /events. GET /players/{playerId}/injuries serves the identical response.

Parameters

GET /players/{playerId}/stoppages — parameters
NameInTypeRequiredNotes
playerIdpathintegeryes
sincequerystringnoWindow start — an ISO date (start of that day) or a UTC instant. Default until minus 180 days.
untilquerystringnoWindow end — an ISO date (the whole of that day) or a UTC instant. Default now.
kindquerystringnoComma-separated row kinds from medical_timeout, trainer_called, toilet_break, pause, stoppage, retirement, walkover. Default medical_timeout,trainer_called,retirement,walkover.
beforequerystringnoThe meta.next_cursor of the previous page (opaque <kind>:<id>), valid for the same window and kinds.
limitqueryintegerno Default 50.

Responses

GET /players/{playerId}/stoppages — responses
StatusMeaning
200The player's stoppage and outcome rows, newest first
400window_too_long, bad_window, bad_since, bad_until, bad_kind or bad_cursor
401Missing, unknown, or disabled credentials
403Your tier doesn't unlock this endpoint
404No roster player holds this id. Carries the archive signpost (detail + see) when the id is a corpus person id.
410The player id existed and was merged into another player record, or was retired with no successor. The body is PlayerMerged, not Error: it carries a forwarding address, which is the whole point of answering 410 rather than 404. An id that was NEVER issued keeps its 404 — a 410 would assert it once existed.
429Rate limit exceeded (Retry-After header present). Three body shapes, told apart by error and scope: the per-MINUTE limit (rate_limited, with upgrade_url, tier and price naming the next tier up); the per-DAY quota (rate_limited with scope: "day", limit_per_day, and resets_at — the absolute ISO instant the daily window resets); and abuse_throttled with retry_at_epoch — a 24-hour block applied to clients that keep hammering far past their cap, which a well-behaved retry loop never sees. Fix the loop rather than retrying through it.

Response fields

GET /players/{playerId}/stoppages — response fields
FieldTypeDescription
dataarray of object
metaobject

Example

curl https://api.livetennisapi.com/api/public/v1/players/1104/stoppages \
  -H "Authorization: Bearer twjp_..."

GET /players/{playerId}/injuries

Alias of /players/{playerId}/stoppages — the identical response (PRO)

Plan required: PRO · operationId: listPlayerInjuries

Added 2026-09-16. The same handler, parameters, rows and meta as /players/{playerId}/stoppages, under the word the request used. The honest name is stoppages: nothing here is a diagnosis, only what the scorer, umpire or result stated.

Parameters

GET /players/{playerId}/injuries — parameters
NameInTypeRequiredNotes
playerIdpathintegeryes
sincequerystringno
untilquerystringno
kindquerystringno
beforequerystringno
limitqueryintegerno Default 50.

Responses

GET /players/{playerId}/injuries — responses
StatusMeaning
200Identical to /players/{playerId}/stoppages
400window_too_long, bad_window, bad_since, bad_until, bad_kind or bad_cursor
401Missing, unknown, or disabled credentials
403Your tier doesn't unlock this endpoint
404No roster player holds this id. Carries the archive signpost (detail + see) when the id is a corpus person id.
410The player id existed and was merged into another player record, or was retired with no successor. The body is PlayerMerged, not Error: it carries a forwarding address, which is the whole point of answering 410 rather than 404. An id that was NEVER issued keeps its 404 — a 410 would assert it once existed.
429Rate limit exceeded (Retry-After header present). Three body shapes, told apart by error and scope: the per-MINUTE limit (rate_limited, with upgrade_url, tier and price naming the next tier up); the per-DAY quota (rate_limited with scope: "day", limit_per_day, and resets_at — the absolute ISO instant the daily window resets); and abuse_throttled with retry_at_epoch — a 24-hour block applied to clients that keep hammering far past their cap, which a well-behaved retry loop never sees. Fix the loop rather than retrying through it.

Response fields

GET /players/{playerId}/injuries — response fields
FieldTypeDescription
dataarray of object
metaobject

Example

curl https://api.livetennisapi.com/api/public/v1/players/1104/injuries \
  -H "Authorization: Bearer twjp_..."

GET /players

Search players by name (FREE)

Plan required: FREE · operationId: searchPlayers

Parameters

GET /players — parameters
NameInTypeRequiredNotes
searchquerystringno
limitqueryintegerno Default 50.
offsetqueryintegerno Default 0.

Responses

GET /players — responses
StatusMeaning
200Players (ranked first; no stats object on the list)
401Missing, unknown, or disabled credentials
429Rate limit exceeded (Retry-After header present). Three body shapes, told apart by error and scope: the per-MINUTE limit (rate_limited, with upgrade_url, tier and price naming the next tier up); the per-DAY quota (rate_limited with scope: "day", limit_per_day, and resets_at — the absolute ISO instant the daily window resets); and abuse_throttled with retry_at_epoch — a 24-hour block applied to clients that keep hammering far past their cap, which a well-behaved retry loop never sees. Fix the loop rather than retrying through it.

Response fields

GET /players — response fields
FieldTypeDescription
dataarray of object
metaobject

Example

curl https://api.livetennisapi.com/api/public/v1/players \
  -H "Authorization: Bearer twjp_..."

GET /players/{playerId}

One player's bio + ranking + cached stats (FREE)

Plan required: FREE · operationId: getPlayer

playerId is a ROSTER id — the live player registry, the id space /players, /matches and /rankings all speak. It is NOT the archive corpus person id. The results archive (1968–2022) keeps its own person registry, and archive match rows publish those ids as winner.player_id / loser.player_id. The two spaces are disjoint: no corpus id resolves here, and since 2026-09-20 a 404 for one says so and points at /history/archive/players?id={playerId}, which is where that id is read. A 404 with no see field is simply an id we do not hold.

Parameters

GET /players/{playerId} — parameters
NameInTypeRequiredNotes
playerIdpathintegeryes

Responses

GET /players/{playerId} — responses
StatusMeaning
200Player with stats ({ratings, season})
401Missing, unknown, or disabled credentials
404No roster player holds this id. When the id IS a corpus person id the body adds detail and see (the archive lookup); otherwise it is the plain Error body.
410The player id existed and was merged into another player record, or was retired with no successor. The body is PlayerMerged, not Error: it carries a forwarding address, which is the whole point of answering 410 rather than 404. An id that was NEVER issued keeps its 404 — a 410 would assert it once existed.
429Rate limit exceeded (Retry-After header present). Three body shapes, told apart by error and scope: the per-MINUTE limit (rate_limited, with upgrade_url, tier and price naming the next tier up); the per-DAY quota (rate_limited with scope: "day", limit_per_day, and resets_at — the absolute ISO instant the daily window resets); and abuse_throttled with retry_at_epoch — a 24-hour block applied to clients that keep hammering far past their cap, which a well-behaved retry loop never sees. Fix the loop rather than retrying through it.

Response fields

GET /players/{playerId} — response fields
FieldTypeDescription
idinteger
namestring
tourstring or nullThe record's OWN tour, which is NOT the tour filter vocabulary. It is granular (juniors_boys, juniors_girls, challenger_men) where the filter is grouped (juniors, challenger), and a doubles team reports it UPPERCASE (ATP) where an individual reports lowercase (atp). Treat it as an opaque string; do not parse it into the filter enum.
countrystring or null
rankinginteger or nullOfficial singles ranking POSITION (the ordinal: 1, 25, 100) — never points, seed, race, UTR, Elo or a doubles rank. Men from the official ATP table, women from the WTA table, chosen by the player (a Challenger or ITF man who holds an ATP ranking carries it here). Refreshed from the standings ahead of each match the player has with us; a player who has not played recently keeps their last refreshed position. null when no ranking is held (doubles teams, most ITF/junior-only players). Always the CURRENT record, even on historical matches — use /rankings?as_of= for the rank in force on a date.
ranking_pointsinteger or nullPoints on the same official table, refreshed with ranking.
ranking_movementstring or null (up, down, same, null)
handstring or null (R, L, null)
backhandinteger or null (1, 2, null)
birthdaystring or null
is_doubles_teamboolean
data_completenessobjectHow much biographical detail is known for this player, so a consumer can distinguish "not in the feed" from "not yet fetched" without probing. Present on every player in a match payload. Lower tours carry far less of it than main tour.
statsobjectSingle-player endpoint only

Example

curl https://api.livetennisapi.com/api/public/v1/players/1104 \
  -H "Authorization: Bearer twjp_..."

GET /tournaments

Tournament catalogue — the id space Match.tournament_id joins (FREE)

Plan required: FREE · operationId: listTournaments

Stable tournament identity, one row per tournament × event type, stable across seasons. city/country come from a curated table and category only where our catalogues agree unambiguously on an exact-name join — each is null otherwise, never derived from the tournament name. Each row also carries the CURRENT season's tier / tier_source (added 2026-09-22) — the level in a closed vocabulary (atp_500, challenger_125, wta_125, itf_w35 … | null); the tier is per season, so a match carries its own.

Parameters

GET /tournaments — parameters
NameInTypeRequiredNotes
searchquerystringnoCase-insensitive substring match on the tournament name.
tourquerystring (atp, wta, challenger, itf, juniors)noRestrict results to one tour. Each value covers its singles and doubles draws, so atp includes ATP doubles and juniors covers the boys' and girls' Grand Slam draws. Omit for all tours. An unrecognised value is a 400 rather than a silent pass-through, so a caller never receives a tour it did not ask for. Applied before pagination, so meta.count reflects the filtered set.
drawquerystring (singles, doubles)noDraw filter (added 2026-08-18) — the axis the tour filter deliberately collapses; the two compose (?tour=itf&draw=doubles is the ITF doubles slice). Same vocabulary as the draw field on Match, decided by the same shared definition, so filter and field cannot disagree. A row whose draw is null — a team tie, or no stated event type and no doubles-team participant — matches NEITHER value: null is an answer, not a wildcard. Two honesty notes: on /tournaments the answer comes from the event type alone (a tournament row has no participants to supply the doubles-team evidence matches have), and draw=doubles alone also returns mixed and exhibition doubles that no tour value reaches. An unknown value is a 400 bad_draw with the allowed values.
tierquerystringnoTier filter (added 2026-09-24): comma-separated exact values from the same closed tier vocabulary as Match.tier (?tier=atp_500, ?tier=atp_500,challenger_100). Selects tournaments whose own tier field carries one of the values, which on this resource is the **current season's** level, because a catalogue row has no season of its own. That is the one difference from ?tier= on /matches and /history/matches, where the value is the level in each MATCH'S season (a tournament keeps one tournament_id while its level moves). Filter and field always read the same season on the same resource, so they cannot disagree. A tournament with no row for the current season matches no value: null means the level is unknown, not absent. Composes with ?tour=, ?draw= and ?search=. Unknown value → 400 bad_tier with the offending values in bad and the full vocabulary in allowed.
limitqueryintegerno Default 50.
offsetqueryintegerno Default 0.

Responses

GET /tournaments — responses
StatusMeaning
200Tournaments, name order
400Bad query parameter
401Missing, unknown, or disabled credentials
429Rate limit exceeded (Retry-After header present). Three body shapes, told apart by error and scope: the per-MINUTE limit (rate_limited, with upgrade_url, tier and price naming the next tier up); the per-DAY quota (rate_limited with scope: "day", limit_per_day, and resets_at — the absolute ISO instant the daily window resets); and abuse_throttled with retry_at_epoch — a 24-hour block applied to clients that keep hammering far past their cap, which a well-behaved retry loop never sees. Fix the loop rather than retrying through it.

Response fields

GET /tournaments — response fields
FieldTypeDescription
dataarray of object
metaobject

Example

curl https://api.livetennisapi.com/api/public/v1/tournaments \
  -H "Authorization: Bearer twjp_..."

GET /tournaments/{tournamentId}

One tournament by its stable id (FREE)

Plan required: FREE · operationId: getTournament

Parameters

GET /tournaments/{tournamentId} — parameters
NameInTypeRequiredNotes
tournamentIdpathstringyesThe tournament_id carried on match objects.

Responses

GET /tournaments/{tournamentId} — responses
StatusMeaning
200The tournament
401Missing, unknown, or disabled credentials
404No such resource, or no data yet
429Rate limit exceeded (Retry-After header present). Three body shapes, told apart by error and scope: the per-MINUTE limit (rate_limited, with upgrade_url, tier and price naming the next tier up); the per-DAY quota (rate_limited with scope: "day", limit_per_day, and resets_at — the absolute ISO instant the daily window resets); and abuse_throttled with retry_at_epoch — a 24-hour block applied to clients that keep hammering far past their cap, which a well-behaved retry loop never sees. Fix the loop rather than retrying through it.

Response fields

GET /tournaments/{tournamentId} — response fields
FieldTypeDescription
idstringThe stable id Match.tournament_id joins.
namestring or null
tourstring or null (atp, wta, challenger, itf, juniors, null)
surfacestring or null (hard, clay, grass, null)Court surface, null where the catalogue holds none — 61 of 10,287 tournaments on 2026-09-20. 35 of those are team competitions (Davis Cup, Billie Jean King Cup, Laver Cup), which have no tournament-level surface to hold: each tie is played on the host nation's choice, and that surface is published on each MATCH of the tie instead. The upstream field for those rows carries the DRAW TIER rather than a surface ("- Preliminary", "- Play Offs", "- Promotion"), and a value that is not one of hard/clay/grass is rejected rather than published. 3 are the exhibition catch-all and 23 are ordinary events the upstream catalogue simply never stated a surface for.
indoorboolean
citystring or nullHost city, from a curated table — null where not curated.
countrystring or nullHost country, ISO-3166 alpha-2 — null where not curated. (NOTE this differs from player.country and the ?country= filter, which use IOC-style lowercase 3-letter codes.)
categorystring or null (grand_slam, masters_1000, tour_finals, atp_500, atp_250, wta_1000, wta_500, wta_250, wta_125, challenger, itf, juniors, null)Tournament category where our catalogues agree unambiguously on an exact-name join — null otherwise, never derived from the name. Unchanged by tier (2026-09-22) and remains the coarse class; read tier for the level.
tierstring or null (grand_slam, atp_finals, atp_1000, atp_500, atp_250, next_gen_finals, united_cup, davis_cup, olympics, laver_cup, wta_finals, wta_elite_trophy, wta_1000, wta_500, wta_250, wta_125, bjk_cup, challenger_175, challenger_125, challenger_100, challenger_75, challenger_50, itf_m15, itf_m25, itf_w15, itf_w25, itf_w35, itf_w40, itf_w50, itf_w60, itf_w75, itf_w80, itf_w100, juniors, utr, exhibition, null)The level this tournament is played at in the CURRENT season (added 2026-09-22) — the same closed vocabulary as tier on a match (grand_slam, atp_1000 … atp_250, wta_1000 … wta_125, challenger_175 … challenger_50, itf_m15 / itf_m25, itf_w15 … itf_w100, the team events, juniors, utr, exhibition). A catalogue row has no season of its own; a match does: the tier is PER SEASON and it moves under one id — Dallas was atp_250 in 2023 and 2024 and atp_500 from 2025; Estoril atp_250, then challenger_175 in 2025, then atp_250 again in 2026; Eastbourne wta_500 through 2024 and wta_250 from 2025 — so read the tier off each MATCH for its season, never copy this value onto a match. Null when this season's calendar does not list the event — never guessed.
tier_sourcestring or null (calendar, name, wikipedia, resolver, null)How tier was established (added 2026-09-22): calendar = the official per-season tour calendar named the level (ATP tour and Challenger calendars, the WTA tournament list); name = an unambiguous name rule (the ITF category is in the event's official name — M25 …, W35 … — team and UTR events are named as such, and the Slam junior draws and exhibitions are their own event type); wikipedia = the season's schedule page, used only where the official calendar could not state the level for that season (Eastbourne 2023–2025); resolver = resolved after the seed dataset, by the same rules, from the event's name, tour and dates in that season. Null exactly when tier is null.

Example

curl https://api.livetennisapi.com/api/public/v1/tournaments/{tournamentId} \
  -H "Authorization: Bearer twjp_..."

GET /rankings

Rankings and Elo — rank-ordered listing (PRO) or per-player as-of records (ULTRA); the as-of Elo tape is ULTRA in both modes

Plan required: PRO · operationId: listRankings

Returns, per ranking system, the newest record effective ON OR BEFORE as_of — never one dated after it. Every other ranking field in this API is the player's CURRENT value joined at read time, so replaying an old match elsewhere shows today's ranks; this endpoint is the point-in-time answer. Systems are never collapsed into a single "rank": ATP/WTA and the ITF circuits carry rank+points, UTR carries a rating with null rank and points because it has neither. meta.coverage.oldest_available gives the earliest date each system can answer for — ITF and UTR observations reach back to 2026-06-01 (append-only per-player history accumulates from 2026-07-29) and nothing earlier can be reconstructed. TWO MODES — with player ids (ULTRA) — the per-player point-in-time records described above. WITHOUT player (PRO) — the FULL published table in rank order for exactly one system, the newest week at or before as_of; rows carry player_name as published and a null player_id for players outside our roster, so the table has no silent holes. utr has no listing (a rating, not a ranking). SYSTEM=UTR — observed ratings, honestly bounded. UTR records are observed from UTR's public search: a rating UTR withholds appears as ABSENT, never as 0, and rating is the only populated value — rank and points are always null. Per-player as-of ONLY — there is deliberately no UTR listing, because a table of only the players we happen to track would be a fake leaderboard. Per-player history accumulates from 2026-07-29; scattered earlier single-snapshot observations reach back to 2026-06-01. Coverage is a deliberate bias, not a roster mirror: the 24-hour sweep targets players with no official rank and no Elo rating (so it skews ITF), and among players active in the last 60 days it holds 931 of 5,606 ITF players (16.6%), 197 of 1,903 Challenger (10.4%), 43 of 573 WTA (7.5%) and 15 of 525 ATP (2.9%) — measured 2026-08-17. SYSTEM=ELO — THE AS-OF ELO TAPE (ULTRA in BOTH modes). Our own computed Elo for 65,622 players on four independent ladders (overall, hard, clay, grass), back to 1877 (ATP) and 1968 (WTA), covering the main tours plus challengers plus the futures tier. It answers what a player was rated BEFORE a given match, which is the only shape a backtest can consume. It is a HISTORICAL TAPE rather than a live leaderboard — the corpus behind it is fixed and no longer receives new results, so meta.coverage.newest_available states the tape's head date on every response (2026-06-15 at publication), and you should read it before treating the table as current. A week's results become effective 14 days after that week begins — strictly after the longest event in tennis — so the failure direction is staleness, never look-ahead. rating is the Elo. rank is LISTING MODE ONLY and is null in per-player mode, because an Elo has no global rank at a past instant until you say which field and which activity window you mean. points is always null. matches is the count on THAT ladder, published so that a rating still near its 1500 cold start is visible rather than inferred. A ladder a player has never played is omitted, never substituted. Ratings are on our own scale, are not comparable with Elo published elsewhere, and do not decay — a surface played only a few weeks a year (grass in particular) moves slowly, so a rising player can sit below an established one for several seasons while beating them. A CURRENT per-player Elo remains free on GET /players/{id}; this is the point-in-time series, the leaderboard and the bulk export. That free rating is sourced differently and sits on a DIFFERENT scale — the two differ by roughly 150 Elo of per-player standard deviation — so never present a rating from one scale against a rating from the other. The Elo listing REQUIRES tour — the ATP and WTA walks are disjoint, so a combined leaderboard would not be comparable — and takes exactly one surface (default overall). elo is never implicit — omitting system returns the official systems only.

Parameters

GET /rankings — parameters
NameInTypeRequiredNotes
playerqueryarray of integernoPlayer id — repeatable, max 50 (ULTRA per-player mode). OMIT it for the rank-ordered listing mode (PRO), which then requires exactly one system.
archive_playerqueryarray of integernoHistorical-corpus person id — repeatable, max 50, system=elo only, and REQUIRES tour because that id is only unique within one tour. It is the same id GET /history/archive/players returns as its id. Use it to reach the ~62,000 rated people who have no row in our player roster; player reaches the rest.
tourquerystring (atp, wta)noREQUIRED for an elo listing and for archive_player. The ATP and WTA Elo walks are disjoint universes whose players never meet, so a combined leaderboard would not be comparable. On an OFFICIAL system it is accepted when it agrees with that system's own tour (system=atp&tour=atp) and REFUSED with 400 when it contradicts it. CORRECTION 2026-09-20: until that date system=atp&tour=wta returned 200 and the ATP table — the parameter was accepted and ignored. If you sent a contradicting pair, the rows you received were the table named by system.
surfacequeryarray of string (overall, hard, clay, grass)nosystem=elo only. Listing mode takes exactly one (default overall); per-player mode is repeatable and defaults to all four ladders. A ladder the player has never played is omitted, never substituted. REFUSED with 400 when no requested system is elo — there is no clay ATP ranking, so the filter cannot be made to act. CORRECTION 2026-09-20: until that date system=atp&surface=clay returned 200 and the OVERALL ATP table. A mixed system=atp&system=elo&surface=clay still passes, because an elo system is present.
min_matchesqueryintegernosystem=elo listing only. Minimum matches on that ladder. Every ladder starts at 1500, so without a floor the top of the table is players who won a handful of matches from the starting rating. Echoed back in meta.coverage.qualified. REFUSED with 400 when no requested system is elo (2026-09-20); it was previously accepted and ignored. Default 20.
activity_weeksqueryintegernosystem=elo listing only. The ladder must have moved within this many weeks of as_of. Elo does not decay, so without an activity window every leaderboard is topped permanently by players who have stopped playing. Echoed back in meta.coverage.qualified. REFUSED with 400 when no requested system is elo (2026-09-20); it was previously accepted and ignored. Default 52.
as_ofquerystringnoYYYY-MM-DD. Omit for the latest known record.
systemqueryarray of string (atp, wta, itf_jt, itf_mt, itf_wt, utr, elo, atp_doubles, wta_doubles)noRestrict to one or more systems. Omit for all of the official systems — elo, atp_doubles and wta_doubles are NEVER included implicitly and must be named (a doubles tie prints as repeated ranks, as the publisher prints it), so an existing request's response is unchanged. Naming a system your plan does not cover refuses the whole call with 403 rather than silently returning the part you are entitled to.
limitqueryintegerno Default 50.
offsetqueryintegerno Default 0.

Responses

GET /rankings — responses
StatusMeaning
200Ranking records in force at as_of
400Bad query parameter
401Missing, unknown, or disabled credentials
403Your tier doesn't unlock this endpoint
429Rate limit exceeded (Retry-After header present). Three body shapes, told apart by error and scope: the per-MINUTE limit (rate_limited, with upgrade_url, tier and price naming the next tier up); the per-DAY quota (rate_limited with scope: "day", limit_per_day, and resets_at — the absolute ISO instant the daily window resets); and abuse_throttled with retry_at_epoch — a 24-hour block applied to clients that keep hammering far past their cap, which a well-behaved retry loop never sees. Fix the loop rather than retrying through it.

Response fields

GET /rankings — response fields
FieldTypeDescription
dataarray of object
metaobject

Example

curl https://api.livetennisapi.com/api/public/v1/rankings \
  -H "Authorization: Bearer twjp_..."