Cricket API Integration in Android Java - Complete Guide 2026
Learn how to integrate CricketLiveApi into your Android Java app. Full guide with Retrofit setup, live scores, scorecards, player stats, webhooks, RecyclerView UI, and real-time score updates.
CricketLiveApi is the fastest way to add live cricket scores, scorecards, player stats, and real-time match updates to your Android Java application. This complete guide walks you through every step from project setup to a fully working live score screen with copy-paste ready code.
What You Will Build
- Fetch and display live cricket matches in a
RecyclerView - Show full scorecard with batter/bowler stats
- Auto-refresh scores every 10 seconds
- Handle API errors gracefully with Retrofit + OkHttp
- Register a webhook endpoint to receive push notifications on score changes
Prerequisites
- Android Studio Hedgehog or newer
- Java 11+ (Android API 26+)
- A free CricketLiveApi key - get one here (no credit card required)
- Basic knowledge of Retrofit and RecyclerView
Step 1 - Get Your API Key
Sign up at cricketliveapi.com/register. After registration your API key is available in the dashboard. The free plan gives you 100 API calls/day - enough to build and test your integration.
Store your key in local.properties (never commit it to Git):
CRICKET_API_KEY=your_api_key_here
Then expose it via build.gradle (app):
def localProps = new Properties()
localProps.load(new FileInputStream(rootProject.file("local.properties")))
android {
defaultConfig {
buildConfigField "String", "CRICKET_API_KEY", "\"${localProps['CRICKET_API_KEY']}\""
}
}
Step 2 - Add Dependencies
In your build.gradle (app):
dependencies {
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation 'com.squareup.okhttp3:logging-interceptor:4.12.0'
implementation 'com.github.bumptech.glide:glide:4.16.0'
implementation 'androidx.recyclerview:recyclerview:1.3.2'
implementation 'androidx.lifecycle:lifecycle-viewmodel:2.7.0'
implementation 'androidx.lifecycle:lifecycle-livedata:2.7.0'
}
Step 3 - Add Internet Permission
In AndroidManifest.xml:
<uses-permission android:name="android.permission.INTERNET" />
Step 4 - Create the Retrofit API Client
Create CricketApiClient.java:
public class CricketApiClient {
private static final String BASE_URL = "https://cricketliveapi.com/api/v1/";
private static Retrofit retrofit;
public static Retrofit getClient(String apiKey) {
if (retrofit == null) {
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(chain -> {
Request request = chain.request().newBuilder()
.header("Authorization", "Bearer " + apiKey)
.header("Accept", "application/json")
.build();
return chain.proceed(request);
}).build();
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}
}
Step 5 - Define the API Interface
Create CricketApiService.java:
public interface CricketApiService {
@GET("matches/live")
Call<LiveMatchesResponse> getLiveMatches();
@GET("matches/upcoming")
Call<MatchesResponse> getUpcomingMatches(@Query("page") int page, @Query("per_page") int perPage);
@GET("matches/recent")
Call<MatchesResponse> getRecentMatches(@Query("page") int page, @Query("per_page") int perPage);
@GET("scorecard/{matchId}/{slug}")
Call<ScorecardResponse> getScorecard(@Path("matchId") int matchId, @Path("slug") String slug);
}
Step 6 - Create Data Models
Create Match.java:
public class Match {
@SerializedName("match_id") public int matchId;
@SerializedName("slug") public String slug;
@SerializedName("title") public String title;
@SerializedName("status") public String status;
@SerializedName("status_detail") public String statusDetail;
@SerializedName("series_name") public String seriesName;
@SerializedName("first_team") public Team firstTeam;
@SerializedName("second_team") public Team secondTeam;
public static class Team {
@SerializedName("name") public String name;
@SerializedName("short_name") public String shortName;
@SerializedName("flag") public String flag;
@SerializedName("score") public String score;
@SerializedName("overs") public String overs;
}
}
public class LiveMatchesResponse {
@SerializedName("status") public String status;
@SerializedName("data") public List<Match> data;
}
Step 7 - ViewModel with Auto-Polling
Create LiveMatchViewModel.java - polls the API every 10 seconds and exposes LiveData to the UI:
public class LiveMatchViewModel extends ViewModel {
private final MutableLiveData<List<Match>> matches = new MutableLiveData<>();
private final MutableLiveData<String> error = new MutableLiveData<>();
private final MutableLiveData<Boolean> loading = new MutableLiveData<>();
private final Handler handler = new Handler(Looper.getMainLooper());
private Runnable pollRunnable;
private static final int POLL_INTERVAL_MS = 10_000;
private CricketApiService apiService;
public void init(String apiKey) {
apiService = CricketApiClient.getClient(apiKey).create(CricketApiService.class);
}
public LiveData<List<Match>> getMatches() { return matches; }
public LiveData<String> getError() { return error; }
public LiveData<Boolean> getLoading() { return loading; }
public void startPolling() {
pollRunnable = new Runnable() {
@Override public void run() {
fetchLiveMatches();
handler.postDelayed(this, POLL_INTERVAL_MS);
}
};
handler.post(pollRunnable);
}
public void stopPolling() {
if (pollRunnable != null) handler.removeCallbacks(pollRunnable);
}
private void fetchLiveMatches() {
loading.setValue(true);
apiService.getLiveMatches().enqueue(new Callback<LiveMatchesResponse>() {
@Override
public void onResponse(Call<LiveMatchesResponse> call, Response<LiveMatchesResponse> response) {
loading.setValue(false);
if (response.isSuccessful() && response.body() != null)
matches.setValue(response.body().data);
else
error.setValue("API error: " + response.code());
}
@Override
public void onFailure(Call<LiveMatchesResponse> call, Throwable t) {
loading.setValue(false);
error.setValue("Network error: " + t.getMessage());
}
});
}
@Override protected void onCleared() { super.onCleared(); stopPolling(); }
}
Step 8 - RecyclerView Adapter
Create MatchAdapter.java:
public class MatchAdapter extends RecyclerView.Adapter<MatchAdapter.MatchViewHolder> {
private List<Match> matchList;
private final OnMatchClickListener listener;
public interface OnMatchClickListener { void onMatchClick(Match match); }
public MatchAdapter(List<Match> list, OnMatchClickListener l) {
this.matchList = list; this.listener = l;
}
public void updateMatches(List<Match> newList) {
this.matchList = newList; notifyDataSetChanged();
}
@Override
public void onBindViewHolder(@NonNull MatchViewHolder h, int pos) {
Match m = matchList.get(pos);
h.tvTitle.setText(m.title);
h.tvStatus.setText(m.statusDetail != null ? m.statusDetail : m.status);
if (m.firstTeam != null) { h.tvTeam1.setText(m.firstTeam.shortName); h.tvScore1.setText(m.firstTeam.score != null ? m.firstTeam.score : "-"); }
if (m.secondTeam != null) { h.tvTeam2.setText(m.secondTeam.shortName); h.tvScore2.setText(m.secondTeam.score != null ? m.secondTeam.score : "-"); }
h.itemView.setOnClickListener(v -> listener.onMatchClick(m));
}
@Override public int getItemCount() { return matchList != null ? matchList.size() : 0; }
}
Step 9 - Wire Up the Activity
In LiveMatchActivity.java:
public class LiveMatchActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_live_match);
RecyclerView rv = findViewById(R.id.recyclerView);
ProgressBar pb = findViewById(R.id.progressBar);
MatchAdapter adapter = new MatchAdapter(new ArrayList<>(), match ->
ScorecardActivity.start(this, match.matchId, match.slug));
rv.setLayoutManager(new LinearLayoutManager(this));
rv.setAdapter(adapter);
LiveMatchViewModel vm = new ViewModelProvider(this).get(LiveMatchViewModel.class);
vm.init(BuildConfig.CRICKET_API_KEY);
vm.getMatches().observe(this, adapter::updateMatches);
vm.getLoading().observe(this, l -> pb.setVisibility(l ? View.VISIBLE : View.GONE));
vm.getError().observe(this, msg -> Toast.makeText(this, msg, Toast.LENGTH_SHORT).show());
vm.startPolling();
}
}
Step 10 - Scorecard with Live Batter/Bowler Block
Create ScorecardResponse.java to map the full API response including the live block:
public class ScorecardResponse {
@SerializedName("status") public String status;
@SerializedName("live") public Live live;
public static class Live {
@SerializedName("batter_striker") public Batter batterStriker;
@SerializedName("batter_non_striker") public Batter batterNonStriker;
@SerializedName("bowler_striker") public Bowler bowlerStriker;
@SerializedName("bowler_non_striker") public Bowler bowlerNonStriker;
@SerializedName("recent_balls") public String recentBalls;
@SerializedName("crr") public String crr;
@SerializedName("rrr") public String rrr;
@SerializedName("target") public String target;
@SerializedName("last_wicket") public String lastWicket;
@SerializedName("partnership") public Partnership partnership;
}
public static class Batter {
@SerializedName("name") public String name;
@SerializedName("runs") public int runs;
@SerializedName("balls") public int balls;
@SerializedName("fours") public int fours;
@SerializedName("sixes") public int sixes;
@SerializedName("strike_rate") public String strikeRate;
}
public static class Bowler {
@SerializedName("name") public String name;
@SerializedName("overs") public String overs;
@SerializedName("runs") public int runs;
@SerializedName("wickets") public int wickets;
@SerializedName("economy") public String economy;
}
public static class Partnership {
@SerializedName("runs") public int runs;
@SerializedName("balls") public int balls;
}
}
Render the live block in ScorecardActivity.java:
private void renderScorecard(ScorecardResponse sc) {
if (sc.live != null) {
if (sc.live.batterStriker != null)
tvBatter.setText(sc.live.batterStriker.name + " " +
sc.live.batterStriker.runs + "(" + sc.live.batterStriker.balls + ")");
if (sc.live.bowlerStriker != null)
tvBowler.setText(sc.live.bowlerStriker.name + " " +
sc.live.bowlerStriker.overs + "-" +
sc.live.bowlerStriker.wickets + "-" +
sc.live.bowlerStriker.runs);
if (sc.live.recentBalls != null) tvBalls.setText(sc.live.recentBalls);
if (sc.live.crr != null) tvCrr.setText("CRR: " + sc.live.crr);
if (sc.live.target != null) tvTarget.setText("Target: " + sc.live.target);
}
}
Step 11 - Webhook Integration (Push Updates)
Instead of polling, register a webhook so CricketLiveApi pushes score changes to your server in real time. Your Android app then receives updates via FCM.
Register a Webhook (one-time, from your backend)
POST https://cricketliveapi.com/api/v1/webhooks
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json
{
"url": "https://yourserver.com/cricket-webhook",
"secret": "your_hmac_secret"
}
Webhook Payload - score_update Event
{
"event": "score_update",
"match_id": 12345,
"slug": "india-vs-australia-1st-test-2026",
"title": "India vs Australia, 1st Test 2026",
"status": "live",
"status_detail": "India need 45 runs from 60 balls",
"first_team": { "name": "India", "short_name": "IND", "score": "280/6", "overs": "45.2" },
"second_team": { "name": "Australia", "short_name": "AUS", "score": "310/8", "overs": "80.0" },
"live": {
"batter_striker": { "name": "Virat Kohli", "runs": 72, "balls": 88, "fours": 8, "sixes": 1, "strike_rate": "81.82" },
"batter_non_striker": { "name": "R. Jadeja", "runs": 34, "balls": 41, "fours": 3, "sixes": 0, "strike_rate": "82.93" },
"bowler_striker": { "name": "P. Cummins", "overs": "18.2", "runs": 62, "wickets": 3, "economy": "3.38" },
"bowler_non_striker": { "name": "M. Starc", "overs": "15.0", "runs": 55, "wickets": 2, "economy": "3.67" },
"recent_balls": "1 . W 4 1 2",
"crr": "6.18",
"rrr": "4.50",
"target": "311",
"last_wicket": "KL Rahul c Smith b Cummins 45",
"partnership": { "runs": 106, "balls": 134 }
}
}
Verify HMAC Signature on Your Server
// Node.js - verify X-Cricket-Signature header
const crypto = require('crypto');
app.post('/cricket-webhook', (req, res) => {
const sig = req.headers['x-cricket-signature'];
const expected = 'sha256=' + crypto
.createHmac('sha256', process.env.WEBHOOK_SECRET)
.update(JSON.stringify(req.body))
.digest('hex');
if (sig !== expected) return res.status(401).send('Invalid signature');
sendFcmToAndroid(req.body);
res.sendStatus(200);
});
Step 12 - Push to Android via Firebase FCM
Once your server receives the webhook, push a data message to your Android app:
// Server-side Node.js
async function sendFcmToAndroid(payload) {
await admin.messaging().send({
topic: 'match_' + payload.match_id,
data: {
event: payload.event,
match_id: String(payload.match_id),
score_team1: payload.first_team?.score || '',
score_team2: payload.second_team?.score || '',
status_detail: payload.status_detail || '',
batter: payload.live?.batter_striker?.name || '',
batter_runs: String(payload.live?.batter_striker?.runs || 0),
bowler: payload.live?.bowler_striker?.name || '',
}
});
}
// Android - subscribe and handle
FirebaseMessaging.getInstance().subscribeToTopic("match_" + matchId);
@Override
public void onMessageReceived(@NonNull RemoteMessage remoteMessage) {
Map<String, String> data = remoteMessage.getData();
Intent intent = new Intent("SCORE_UPDATE");
intent.putExtra("score1", data.get("score_team1"));
intent.putExtra("score2", data.get("score_team2"));
intent.putExtra("batter", data.get("batter") + " " + data.get("batter_runs"));
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
}
Available API Endpoints - Quick Reference
| Method | Endpoint | Description |
|---|---|---|
GET | /api/v1/matches/live | All live matches |
GET | /api/v1/matches/upcoming | Upcoming matches (paginated) |
GET | /api/v1/matches/recent | Recent/completed matches |
GET | /api/v1/scorecard/{matchId}/{slug} | Full scorecard + live block |
GET | /api/v1/series | All series |
GET | /api/v1/players/{id} | Player profile and stats |
POST | /api/v1/webhooks | Register webhook endpoint |
GET | /api/v1/webhooks | List registered webhooks |
DELETE | /api/v1/webhooks/{id} | Delete a webhook |
Caching Best Practices for Android
- Live matches - poll every 10 seconds; cache in ViewModel memory (survives rotation)
- Scorecard - cache in SharedPreferences or Room for 15 seconds; show stale data while refreshing
- Series / player data - cache in Room for 1 hour; these change rarely
- OkHttp disk cache - add a 10 MB cache for non-live endpoints to reduce data usage
File cacheDir = new File(context.getCacheDir(), "cricket_http_cache");
Cache cache = new Cache(cacheDir, 10 * 1024 * 1024); // 10 MB
OkHttpClient client = new OkHttpClient.Builder()
.cache(cache)
.addInterceptor(/* auth interceptor */)
.build();
Error Handling
Always handle these common HTTP error codes in your Retrofit callbacks:
switch (response.code()) {
case 200: matches.setValue(response.body().data); break;
case 401: error.setValue("Invalid API key. Check cricketliveapi.com/dashboard"); break;
case 429: error.setValue("Rate limit exceeded. Upgrade at cricketliveapi.com/pricing"); break;
case 503:
error.setValue("Service unavailable. Retrying...");
handler.postDelayed(() -> fetchLiveMatches(), 5000);
break;
default: error.setValue("Unexpected error: " + response.code());
}
Use the AI Assistant in the API Docs
Not sure which endpoint to use or how to parse a response? The CricketLiveApi documentation includes a built-in AI assistant that answers your questions in real time.
- Ask "How do I get the current batter strike rate in Android?"
- Ask "Show me a Retrofit model for the scorecard response"
- Ask "What does the live block contain for a T20 match?"
- Ask "How do I verify the webhook HMAC signature in Java?"
The AI assistant reads the live API documentation and gives you accurate, up-to-date answers - no more digging through docs manually.
Open API Docs & AI Assistant →
Frequently Asked Questions
Can I use CricketLiveApi in a free Android app?
Yes. The free plan gives you 100 API calls/day - enough for development and small apps. Paid plans start at affordable rates for production apps with higher traffic.
Does the API support all cricket formats?
Yes - Test, ODI, T20I, IPL, BBL, PSL, and all major domestic tournaments are covered.
How do I get batter and bowler data in Android?
Call GET /api/v1/scorecard/{matchId}/{slug} and read the live object from the response. It contains batter_striker, batter_non_striker, bowler_striker, bowler_non_striker, recent_balls, crr, rrr, target, and last_wicket.
Is polling or webhooks better for an Android app?
For most apps, polling every 10 seconds is the simplest approach. Webhooks + FCM give you true real-time updates with zero battery drain from polling - ideal for production apps with many concurrent users.
What is the AI assistant in the API docs?
The API documentation has a built-in AI chat assistant. You can ask it questions like "show me the Android Retrofit model for the scorecard endpoint" and it will generate the exact code you need based on the live documentation.