NZ Election Forecast

New Zealand Election Forecast

Aggregating every major New Zealand poll into one live forecast of the 2026 election.

Updated 4 August 2026 · Latest addition: Roy Morgan poll
Live forecast
53.9%
chance the Coalition retains government
ACT · National · NZ First, from 50,000 simulations of the current polling average
OppositionLabour · Greens · Māori
20.05%
Disputed outcomeNo majority
26.04%
Government
Opposition
Disputed Outcome
Updated · 4 Aug 2026
Threshold watch
44.26%
chance The Opportunities Party enters Parliament
About a 1-in-2 shot, from the same 50,000 simulations. TOP needs 5% of the party vote — or to win a single electorate.

A separate question from the government odds above — these don't add up together. Miss on both routes and TOP takes no seats at all, and its votes then count toward no one, which reshapes every other party's seat total.

Polling

The polls - Aggregated

Sources: Curia · Talbot Mills · Roy Morgan · Reid Research · Verian · Freshwater
Aquileo | New Zealand Polling Tracker
Latest polls

New Zealand Polling Tracker

Aggregated party support from major polling organisations

Pollster
Parties
${s.avg.toFixed(1)}%
${arrow(s.change)} · ±${s.margin}
`; c.appendChild(card); }); } /* ── Legend ── */ function buildLegend() { const c = document.getElementById('nzptLegend'); c.innerHTML = '
Parties · tap to toggle
'; parties.forEach(party => { const cfg = partyConfig[party]; const s = latest(party); const item = document.createElement('div'); item.className = 'nzpt-legend-item' + (visibleParties.has(party) ? '' : ' inactive'); item.innerHTML = ` ${cfg.name} ${s.avg.toFixed(1)}%`; item.addEventListener('click', () => { if (visibleParties.has(party)) { if (visibleParties.size === 1) return; // keep at least one party on visibleParties.delete(party); } else visibleParties.add(party); item.classList.toggle('inactive'); buildSummary(); updateChart(); }); c.appendChild(item); }); } /* ── Chart ── */ const ctx = document.getElementById('nzptChart').getContext('2d'); function buildDatasets() { const mobile = isMobile(); const ds = []; parties.forEach(party => { if (!visibleParties.has(party)) return; const color = partyConfig[party].color; const s = cache.out[party]; if (showBand) { ds.push({ label: party + '_u', data: s.upper, borderColor: 'transparent', backgroundColor: 'transparent', pointRadius: 0, fill: false, tension: 0.35, spanGaps: false, order: 6 }); ds.push({ label: party + '_b', data: s.lower, borderColor: 'transparent', backgroundColor: color + '12', pointRadius: 0, fill: '-1', tension: 0.35, spanGaps: false, order: 6 }); } ds.push({ label: partyConfig[party].name, data: s.trend, borderColor: color, backgroundColor: color, borderWidth: mobile ? 2.25 : 2.75, pointRadius: 0, pointHoverRadius: mobile ? 4 : 5, pointHoverBackgroundColor: color, pointHoverBorderColor: '#fff', pointHoverBorderWidth: 2, tension: 0.35, fill: false, spanGaps: false, order: 1 }); }); return ds; } // Draws the 5% MMP threshold, the individual poll dots, and the hover crosshair. const overlay = { id: 'nzptOverlay', beforeDatasetsDraw(c) { const { ctx, chartArea: { left, right, top, bottom }, scales: { y } } = c; const yp = y.getPixelForValue(5); if (yp >= top && yp { if (!visibleParties.has(party)) return; ctx.save(); ctx.fillStyle = partyConfig[party].color + '52'; getFiltered().forEach(d => { if (d[party] == null) return; const px = x.getPixelForValue(+new Date(d.date)); const py = y.getPixelForValue(d[party]); if (py bottom) return; ctx.beginPath(); ctx.arc(px, py, r, 0, Math.PI * 2); ctx.fill(); }); ctx.restore(); }); } const yp = y.getPixelForValue(5); if (yp >= top && yp 0 && t.getActiveElements && t.getActiveElements().length) { ctx.save(); ctx.beginPath(); ctx.moveTo(t.caretX, top); ctx.lineTo(t.caretX, bottom); ctx.lineWidth = 1; ctx.setLineDash([4, 4]); ctx.strokeStyle = 'rgba(15,23,42,0.18)'; ctx.stroke(); ctx.restore(); } } }; function chartOpts() { const mobile = isMobile(); return { responsive: true, maintainAspectRatio: false, interaction: { mode: 'index', intersect: false, axis: 'x' }, plugins: { legend: { display: false }, tooltip: { filter: i => !i.dataset.label.includes('_') && i.parsed.y != null, itemSort: (a, b) => b.parsed.y - a.parsed.y, backgroundColor: '#0f172a', titleColor: '#f8fafc', bodyColor: '#cbd5e1', borderColor: '#334155', borderWidth: 1, padding: { top: 11, bottom: 11, left: 14, right: 16 }, cornerRadius: 10, titleFont: { size: 12, weight: '600', family: "'DM Sans', sans-serif" }, titleMarginBottom: 8, bodyFont: { size: 12, family: "'JetBrains Mono', monospace" }, bodySpacing: 6, displayColors: true, boxWidth: 8, boxHeight: 8, boxPadding: 6, usePointStyle: true, callbacks: { title: items => { if (!items.length) return ''; return new Date(items[0].parsed.x).toLocaleDateString('en-NZ', { day: 'numeric', month: 'short', year: 'numeric' }); }, label: i => { const name = i.dataset.label; const pad = name + ' '.repeat(Math.max(1, 14 - name.length)); return pad + i.parsed.y.toFixed(1) + '%'; } } } }, scales: { x: { type: 'time', time: { unit: mobile ? 'quarter' : 'month', displayFormats: { month: 'MMM yy', quarter: 'MMM yy' } }, grid: { display: false }, border: { display: false }, ticks: { color: '#94a3b8', font: { size: mobile ? 10 : 11, weight: '500', family: "'DM Sans', sans-serif" }, maxRotation: 0, maxTicksLimit: mobile ? 5 : 10, padding: 8 } }, y: { min: 0, max: yMax(), grid: { color: '#f1f5f9', lineWidth: 1 }, border: { display: false }, ticks: { color: '#94a3b8', font: { size: mobile ? 10 : 11, weight: '500', family: "'DM Sans', sans-serif" }, stepSize: 10, padding: 12, callback: v => v + '%' } } }, animation: { duration: 500, easing: 'easeOutQuart' } }; } let chart; function updateChart() { chart.data.datasets = buildDatasets(); chart.options = chartOpts(); chart.update('none'); } /* ── Header meta ── */ function setMeta() { const last = new Date(data[data.length - 1].date); const first = new Date(data[0].date); const fmt = (d, o) => d.toLocaleDateString('en-NZ', o); document.getElementById('nzptEyebrow').textContent = 'Updated ' + fmt(last, { day: 'numeric', month: 'short', year: 'numeric' }); document.getElementById('nzptSub').textContent = `Rolling 30-day average from ${data.length} polls · ${fmt(first, { month: 'short', year: 'numeric' })} – ${fmt(last, { month: 'short', year: 'numeric' })}`; } /* ── Resize ── */ let resizeTimer; window.addEventListener('resize', () => { clearTimeout(resizeTimer); resizeTimer = setTimeout(updateChart, 200); }); /* ── Init ── */ cache = buildAll(getFiltered()); chart = new Chart(ctx, { type: 'line', data: { datasets: buildDatasets() }, options: chartOpts(), plugins: [overlay] }); setMeta(); buildSummary(); buildLegend(); })();
Aquileo | NZ Polling Data
Year
Lead
Date Pollster n NAT LAB GRN ACT NZF TPM TOP Others Lead
Markets

Prediction markets

Real-money odds from Polymarket, shown for comparison
Will the next governing coalition of New Zealand include National + ACT + NZF?
Yes 27% · No 73%
View full market & trade on Polymarket
Will the next governing coalition of New Zealand include Labour + Green + Maori?
Yes 27% · No 74%
View full market & trade on Polymarket
Will Christopher Luxon be the next Prime Minister of New Zealand after the 2026 elections?
Yes 44% · No 56%
View full market & trade on Polymarket
Will Chris Hipkins be the next Prime Minister of New Zealand after the 2026 elections?
Yes 55% · No 46%
View full market & trade on Polymarket

It is illegal for prediction markets such as Polymarket to allow New Zealanders to place bets on them, and the Kiwi Polls model may not reflect current market sentiment. These markets currently have low trading volume, so prices can swing sharply.

Election news

Latest from the campaign

Headlines from around NZ media — links open the original article

Follow the race

New polls land all campaign. Get the model's read the moment they do.