Jack Grealish pulled one back after the break and an own-goal by Vladimir Coufal hauled City level at the London Stadium.
Jarrod Bowen’s first-half double threatened to hand the initiative to second-placed Liverpool, who would have surely relished the challenge of drawing level on points and gnawing away at the goal difference at Southampton on Tuesday night.
But Jack Grealish pulled one back after the break and an own goal by Vladimir Coufal hauled City level to at least keep their title destiny in their hands.
Pep Guardiola’s side were then awarded a spot-kick which, had they scored it, would have left them needing just a point against Aston Villa next weekend to retain the Premier League crown.
But Lukasz Fabianski saved Riyad Mahrez’s effort to ensure the race at the top will go to the wire.
It was a dramatic end to a crazy match, which began in party mode before kick-off as West Ham bid an emotional farewell to club captain Mark Noble.
The tearful 35-year-old soaked up the acclaim from all around the ground including, bizarrely, Prince Albert II of Monaco, a friend of the midfielder.
With a soliloquy from East End royalty Danny Dyer, a giant number 16 shirt in the middle of the pitch and cards spelling out his name in the stands, West Ham were not going to let one of their favourite sons leave quietly after 18 years of service.
But not even the man who has seen it all and done it all in claret and blue would have predicted what was to come in a quite barmy game.
The carnival atmosphere was cranked up even further in the 24th minute when Bowen beat the City offside trap as he raced onto Pablo Fornals’ ball over the top.
The England hopeful won his foot race with Aymeric Laporte but still had plenty to do, keeping his nerve to round City keeper Ederson and finish superbly from the angle.
Back came City but West Ham kept them at bay, with Fabianski tipping Laporte’s drive over.
Gabriel Jesus and Oleksandr Zinchenko fired wide and Fabianksi held Kevin De Bruyne’s free-kick.
Then City were hit by another sucker punch before half-time, Bowen sent scampering away again by Tomas Soucek and burying a low finish past Ederson.
City needed a quick response, and they got it when West Ham failed to clear their lines shortly after half-time.
Rodri flicked the high loose ball to Grealish whose shot bounced into the ground, off the thigh of Dawson and high into the net.
Bernardo Silva looked set to haul City level moments later but Fabianski kept out his volley at the near post.
But City were still shooting themselves in the foot at the back and when Michail Antonio robbed Zinchenko on the byline he set up Bowen, who hit the side netting.
Then Fernandinho’s backpass fell straight to Antonio, who made a mess of his attempted lob to give City another let-off.
They took full advantage when Mahrez swung in a free-kick which the diving Coufal headed into his own net.
Mahrez had the chance to win it with six minutes remaining after Dawson brought down Jesus, but the Algerian’s spot-kick was well saved by Fabianski and City had to settle for a point.