Milan travelled to the Castellani stadium buoyed after a 2-0 defeat of Fiorentina last week that left Sinisa Mihajlovic's men 12 points behind leaders Napoli and within sight of the European qualifying places.
But the visitors had left themselves exposed on the half hour when Empoli forward Piotr Zielinski was unmarked to level Carlos Bacca's eighth-minute opener.
And the Milan defence was at fault on the hour when veteran striker Massimo Maccarone pounced at the back post to convert a rebound from teenaged goalkeeper Gianluigi Donnarumma to level again after Giacomo Bonaventura had restored their lead on 48 minutes.
Milan's sixth draw of the season left them in sixth place, with Empoli one point and two places further adrift.
Mihajlovic kept faith with the side that stunned Fiorentina at the San Siro last week, when Balotelli made a five-minute cameo appearance from the bench following his recent return from a lengthy injury lay-off.
Yet it took only eight minutes for the visitors to break the deadlock, Bacca collecting Luca Antonelli's punt from midfield to run between two defenders and beat onrushing goalkeeper Lukasz Skorupski at his near post.
Empoli thought they had levelled when Maccarone's glancing header from a free-kick beat 16-year-old Donnarumma but he had strayed just offside.
The hosts had reason to celebrate just after the half hour with a well-worked move that drew Milan's defence to a decoy runner, allowing Zielinski a clear run on to Riccardo Saponara's ball to beat Donnarumma at his far post.
Milan replied soon after but Skorupski plucked M'Baye Niang's effort out from under the bar, while Bonaventura's free-kick was a yard over the crossbar.
The visitors spurned a great chance on the stroke of half-time when Antonelli's weighted cross flew along the six-yard box but there were no takers. It fell to Bonaventura, who blasted over.
Bonaventura made up for that miss three minutes after the restart with arguably the luckiest goal of the season so far.
An Empoli clearance smacked off the head of Niang, and as the Frenchman fell to the ground in pain the ball continued towards Bonaventura, who pounced to beat the onrushing Skorupski with a crisp strike from eight yards.
Milan's joy, however, was short-lived. Maccarone made up for his close call earlier by firing a Donnarumma rebound past the prone keeper on 61 minutes.
It was the striker's ninth goal of the season and his third since setting social media alight after celebrating a pre-Christmas goal against Bologna by running to the sidelines to sip from a pint of lager.
Balotelli replaced Niang with 25 minutes remaining but the striker's first opportunity was a tame free-kick from 30 metres that was straight at Skorupski.
Keisuke Honda required a headband for a cut from a mid-air clash but the Japanese midfielder made way for Kevin-Prince Boateng with four minutes left to play.
Milan spurned two late half-chances to snatch the win.
Balotelli should have done better with a cross that met traffic as Bacca was running towards goal.
In injury time the Colombian striker did well to steal possession in midfield but lost the ball on the edge of the area with Balotelli free to his right.
Champions Juventus, who sit two points behind leaders Napoli ahead of their visit to Sampdoria, are aiming for their 11th consecutive win when they host Roma in Turin Sunday.
Roma, who have one win in their last six outings, are fifth at nine points off the pace.